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