Merge from emacs-23; up to 2010-06-03T05:41:49Z!rgm@gnu.org.
[bpt/emacs.git] / lisp / progmodes / octave-inf.el
1 ;;; octave-inf.el --- running Octave as an inferior Emacs process
2
3 ;; Copyright (C) 1997, 2001-2011 Free Software Foundation, Inc.
4
5 ;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
6 ;; Author: John Eaton <jwe@bevo.che.wisc.edu>
7 ;; Maintainer: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
8 ;; Keywords: languages
9 ;; Package: octave-mod
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
25
26 ;;; Commentary:
27
28 ;;; Code:
29
30 (require 'octave-mod)
31 (require 'comint)
32
33 (defgroup octave-inferior nil
34 "Running Octave as an inferior Emacs process."
35 :group 'octave)
36
37 (defcustom inferior-octave-program "octave"
38 "Program invoked by `inferior-octave'."
39 :type 'string
40 :group 'octave-inferior)
41
42 (defcustom inferior-octave-prompt
43 "\\(^octave\\(\\|.bin\\|.exe\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
44 "Regexp to match prompts for the inferior Octave process."
45 :type 'regexp
46 :group 'octave-inferior)
47
48 (defcustom inferior-octave-startup-file nil
49 "Name of the inferior Octave startup file.
50 The contents of this file are sent to the inferior Octave process on
51 startup."
52 :type '(choice (const :tag "None" nil)
53 file)
54 :group 'octave-inferior)
55
56 (defcustom inferior-octave-startup-args nil
57 "List of command line arguments for the inferior Octave process.
58 For example, for suppressing the startup message and using `traditional'
59 mode, set this to (\"-q\" \"--traditional\")."
60 :type '(repeat string)
61 :group 'octave-inferior)
62
63 (defvar inferior-octave-mode-map
64 (let ((map (make-sparse-keymap)))
65 (set-keymap-parent map comint-mode-map)
66 (define-key map "\t" 'comint-dynamic-complete)
67 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
68 (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
69 (define-key map [menu-bar inout list-history]
70 '("List Input History" . inferior-octave-dynamic-list-input-ring))
71 (define-key map "\C-c\C-h" 'octave-help)
72 map)
73 "Keymap used in Inferior Octave mode.")
74
75 (defvar inferior-octave-mode-syntax-table
76 (let ((table (make-syntax-table)))
77 (modify-syntax-entry ?\` "w" table)
78 (modify-syntax-entry ?\# "<" table)
79 (modify-syntax-entry ?\n ">" table)
80 table)
81 "Syntax table in use in inferior-octave-mode buffers.")
82
83 (defcustom inferior-octave-mode-hook nil
84 "*Hook to be run when Inferior Octave mode is started."
85 :type 'hook
86 :group 'octave-inferior)
87
88 (defvar inferior-octave-font-lock-keywords
89 (list
90 (cons inferior-octave-prompt 'font-lock-type-face))
91 ;; Could certainly do more font locking in inferior Octave ...
92 "Additional expressions to highlight in Inferior Octave mode.")
93
94
95 ;;; Compatibility functions
96 (if (not (fboundp 'comint-line-beginning-position))
97 ;; comint-line-beginning-position is defined in Emacs 21
98 (defun comint-line-beginning-position ()
99 "Returns the buffer position of the beginning of the line, after any prompt.
100 The prompt is assumed to be any text at the beginning of the line matching
101 the regular expression `comint-prompt-regexp', a buffer local variable."
102 (save-excursion (comint-bol nil) (point))))
103
104
105 (defvar inferior-octave-output-list nil)
106 (defvar inferior-octave-output-string nil)
107 (defvar inferior-octave-receive-in-progress nil)
108
109 (defvar inferior-octave-startup-hook nil)
110
111 (defvar inferior-octave-complete-impossible nil
112 "Non-nil means that `inferior-octave-complete' is impossible.")
113
114 (defvar inferior-octave-has-built-in-variables nil
115 "Non-nil means that Octave has built-in variables.")
116
117 (defvar inferior-octave-dynamic-complete-functions
118 '(inferior-octave-complete comint-dynamic-complete-filename)
119 "List of functions called to perform completion for inferior Octave.
120 This variable is used to initialize `comint-dynamic-complete-functions'
121 in the Inferior Octave buffer.")
122
123 (define-derived-mode inferior-octave-mode comint-mode "Inferior Octave"
124 "Major mode for interacting with an inferior Octave process.
125 Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
126 buffer.
127
128 Entry to this mode successively runs the hooks `comint-mode-hook' and
129 `inferior-octave-mode-hook'."
130 (setq comint-prompt-regexp inferior-octave-prompt
131 mode-line-process '(":%s")
132 local-abbrev-table octave-abbrev-table)
133
134 (set (make-local-variable 'comment-start) octave-comment-start)
135 (set (make-local-variable 'comment-end) "")
136 (set (make-local-variable 'comment-column) 32)
137 (set (make-local-variable 'comment-start-skip) octave-comment-start-skip)
138
139 (set (make-local-variable 'font-lock-defaults)
140 '(inferior-octave-font-lock-keywords nil nil))
141
142 (setq comint-input-ring-file-name
143 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
144 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024))
145 (set (make-local-variable 'comint-dynamic-complete-functions)
146 inferior-octave-dynamic-complete-functions)
147 (add-hook 'comint-input-filter-functions
148 'inferior-octave-directory-tracker nil t)
149 (comint-read-input-ring t))
150
151 ;;;###autoload
152 (defun inferior-octave (&optional arg)
153 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
154 This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
155
156 Unless ARG is non-nil, switches to this buffer.
157
158 The elements of the list `inferior-octave-startup-args' are sent as
159 command line arguments to the inferior Octave process on startup.
160
161 Additional commands to be executed on startup can be provided either in
162 the file specified by `inferior-octave-startup-file' or by the default
163 startup file, `~/.emacs-octave'."
164 (interactive "P")
165 (let ((buffer inferior-octave-buffer))
166 (get-buffer-create buffer)
167 (if (comint-check-proc buffer)
168 ()
169 (with-current-buffer buffer
170 (comint-mode)
171 (inferior-octave-startup)
172 (inferior-octave-mode)))
173 (if (not arg)
174 (pop-to-buffer buffer))))
175
176 ;;;###autoload
177 (defalias 'run-octave 'inferior-octave)
178
179 (defun inferior-octave-startup ()
180 "Start an inferior Octave process."
181 (let ((proc (comint-exec-1
182 (substring inferior-octave-buffer 1 -1)
183 inferior-octave-buffer
184 inferior-octave-program
185 (append (list "-i" "--no-line-editing")
186 inferior-octave-startup-args))))
187 (set-process-filter proc 'inferior-octave-output-digest)
188 (setq comint-ptyp process-connection-type
189 inferior-octave-process proc
190 inferior-octave-output-list nil
191 inferior-octave-output-string nil
192 inferior-octave-receive-in-progress t)
193
194 ;; This may look complicated ... However, we need to make sure that
195 ;; we additional startup code only AFTER Octave is ready (otherwise,
196 ;; output may be mixed up). Hence, we need to digest the Octave
197 ;; output to see when it issues a prompt.
198 (while inferior-octave-receive-in-progress
199 (accept-process-output inferior-octave-process))
200 (goto-char (point-max))
201 (set-marker (process-mark proc) (point))
202 (insert-before-markers
203 (concat
204 (if (not (bobp)) "\f\n")
205 (if inferior-octave-output-list
206 (concat (mapconcat
207 'identity inferior-octave-output-list "\n")
208 "\n"))))
209
210 ;; Find out whether Octave has built-in variables.
211 (inferior-octave-send-list-and-digest
212 (list "exist \"LOADPATH\"\n"))
213 (setq inferior-octave-has-built-in-variables
214 (string-match "101$" (car inferior-octave-output-list)))
215
216 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
217 ;; means trouble.
218 (inferior-octave-send-list-and-digest (list "PS2\n"))
219 (if (string-match "\\(PS2\\|ans\\) = *$" (car inferior-octave-output-list))
220 (inferior-octave-send-list-and-digest
221 (list (if inferior-octave-has-built-in-variables
222 "PS2 = \"> \"\n"
223 "PS2 (\"> \");\n"))))
224
225 ;; O.k., now we are ready for the Inferior Octave startup commands.
226 (let* (commands
227 (program (file-name-nondirectory inferior-octave-program))
228 (file (or inferior-octave-startup-file
229 (concat "~/.emacs-" program))))
230 (setq commands
231 (list "more off;\n"
232 (if (not (string-equal
233 inferior-octave-output-string ">> "))
234 (if inferior-octave-has-built-in-variables
235 "PS1=\"\\\\s> \";\n"
236 "PS1 (\"\\\\s> \");\n"))
237 (if (file-exists-p file)
238 (format "source (\"%s\");\n" file))))
239 (inferior-octave-send-list-and-digest commands))
240 (insert-before-markers
241 (concat
242 (if inferior-octave-output-list
243 (concat (mapconcat
244 'identity inferior-octave-output-list "\n")
245 "\n"))
246 inferior-octave-output-string))
247 ;; Next, we check whether Octave supports `completion_matches' ...
248 (inferior-octave-send-list-and-digest
249 (list "exist \"completion_matches\"\n"))
250 (setq inferior-octave-complete-impossible
251 (not (string-match "5$" (car inferior-octave-output-list))))
252
253 ;; And finally, everything is back to normal.
254 (set-process-filter proc 'inferior-octave-output-filter)
255 (run-hooks 'inferior-octave-startup-hook)
256 (run-hooks 'inferior-octave-startup-hook)
257 ;; Just in case, to be sure a cd in the startup file
258 ;; won't have detrimental effects.
259 (inferior-octave-resync-dirs)))
260
261 \f
262 (defun inferior-octave-complete ()
263 "Perform completion on the Octave symbol preceding point.
264 This is implemented using the Octave command `completion_matches' which
265 is NOT available with versions of Octave prior to 2.0."
266 (interactive)
267 (let* ((end (point))
268 (command
269 (save-excursion
270 (skip-syntax-backward "w_" (comint-line-beginning-position))
271 (buffer-substring-no-properties (point) end)))
272 (proc (get-buffer-process inferior-octave-buffer)))
273 (cond (inferior-octave-complete-impossible
274 (error (concat
275 "Your Octave does not have `completion_matches'. "
276 "Please upgrade to version 2.X.")))
277 ((string-equal command "")
278 (message "Cannot complete an empty string"))
279 (t
280 (inferior-octave-send-list-and-digest
281 (list (concat "completion_matches (\"" command "\");\n")))
282 ;; Sort the list
283 (setq inferior-octave-output-list
284 (sort inferior-octave-output-list 'string-lessp))
285 ;; Remove duplicates
286 (let* ((x inferior-octave-output-list)
287 (y (cdr x)))
288 (while y
289 (if (string-equal (car x) (car y))
290 (setcdr x (setq y (cdr y)))
291 (setq x y
292 y (cdr y)))))
293 ;; And let comint handle the rest
294 (comint-dynamic-simple-complete
295 command inferior-octave-output-list)))))
296
297 (defun inferior-octave-dynamic-list-input-ring ()
298 "List the buffer's input history in a help buffer."
299 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
300 ;; "completion" by "history reference" ...
301 (interactive)
302 (if (or (not (ring-p comint-input-ring))
303 (ring-empty-p comint-input-ring))
304 (message "No history")
305 (let ((history nil)
306 (history-buffer " *Input History*")
307 (index (1- (ring-length comint-input-ring)))
308 (conf (current-window-configuration)))
309 ;; We have to build up a list ourselves from the ring vector.
310 (while (>= index 0)
311 (setq history (cons (ring-ref comint-input-ring index) history)
312 index (1- index)))
313 ;; Change "completion" to "history reference"
314 ;; to make the display accurate.
315 (with-output-to-temp-buffer history-buffer
316 (display-completion-list history)
317 (set-buffer history-buffer))
318 (message "Hit space to flush")
319 (let ((ch (read-event)))
320 (if (eq ch ?\ )
321 (set-window-configuration conf)
322 (setq unread-command-events (list ch)))))))
323
324 (defun inferior-octave-strip-ctrl-g (string)
325 "Strip leading `^G' character.
326 If STRING starts with a `^G', ring the bell and strip it."
327 (if (string-match "^\a" string)
328 (progn
329 (ding)
330 (setq string (substring string 1))))
331 string)
332
333 (defun inferior-octave-output-filter (proc string)
334 "Standard output filter for the inferior Octave process.
335 Ring Emacs bell if process output starts with an ASCII bell, and pass
336 the rest to `comint-output-filter'."
337 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
338
339 (defun inferior-octave-output-digest (proc string)
340 "Special output filter for the inferior Octave process.
341 Save all output between newlines into `inferior-octave-output-list', and
342 the rest to `inferior-octave-output-string'."
343 (setq string (concat inferior-octave-output-string string))
344 (while (string-match "\n" string)
345 (setq inferior-octave-output-list
346 (append inferior-octave-output-list
347 (list (substring string 0 (match-beginning 0))))
348 string (substring string (match-end 0))))
349 (if (string-match inferior-octave-prompt string)
350 (setq inferior-octave-receive-in-progress nil))
351 (setq inferior-octave-output-string string))
352
353 (defun inferior-octave-send-list-and-digest (list)
354 "Send LIST to the inferior Octave process and digest the output.
355 The elements of LIST have to be strings and are sent one by one. All
356 output is passed to the filter `inferior-octave-output-digest'."
357 (let* ((proc inferior-octave-process)
358 (filter (process-filter proc))
359 string)
360 (set-process-filter proc 'inferior-octave-output-digest)
361 (setq inferior-octave-output-list nil)
362 (unwind-protect
363 (while (setq string (car list))
364 (setq inferior-octave-output-string nil
365 inferior-octave-receive-in-progress t)
366 (comint-send-string proc string)
367 (while inferior-octave-receive-in-progress
368 (accept-process-output proc))
369 (setq list (cdr list)))
370 (set-process-filter proc filter))))
371
372 (defun inferior-octave-directory-tracker (string)
373 "Tracks `cd' commands issued to the inferior Octave process.
374 Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
375 (cond
376 ((string-match "^[ \t]*cd[ \t;]*$" string)
377 (cd "~"))
378 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
379 (cd (substring string (match-beginning 1) (match-end 1))))))
380
381 (defun inferior-octave-resync-dirs ()
382 "Resync the buffer's idea of the current directory.
383 This command queries the inferior Octave process about its current
384 directory and makes this the current buffer's default directory."
385 (interactive)
386 (inferior-octave-send-list-and-digest '("disp (pwd ())\n"))
387 (cd (car inferior-octave-output-list)))
388
389 ;;; provide ourself
390
391 (provide 'octave-inf)
392
393 ;;; octave-inf.el ends here