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