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