(defgroup reftex): Update home page url-link.
[bpt/emacs.git] / lisp / progmodes / octave-inf.el
CommitLineData
092af6d8 1;;; octave-inf.el --- running Octave as an inferior Emacs process
081bf30e 2
d91362c9 3;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006
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
RS
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
3a35cf56
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
081bf30e 27
e8af40ee
PJ
28;;; Commentary:
29
081bf30e
RS
30;;; Code:
31
f7bbab75 32(require 'octave-mod)
081bf30e
RS
33(require 'comint)
34
28d16ed3
AS
35(defgroup octave-inferior nil
36 "Running Octave as an inferior Emacs process."
37 :group 'octave)
081bf30e 38
28d16ed3 39(defcustom inferior-octave-program "octave"
e2536e57 40 "Program invoked by `inferior-octave'."
28d16ed3
AS
41 :type 'string
42 :group 'octave-inferior)
43
44(defcustom inferior-octave-prompt
b2fc0976 45 "\\(^octave\\(\\|.bin\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
e2536e57 46 "Regexp to match prompts for the inferior Octave process."
28d16ed3
AS
47 :type 'regexp
48 :group 'octave-inferior)
081bf30e 49
28d16ed3 50(defcustom inferior-octave-startup-file nil
e2536e57 51 "Name of the inferior Octave startup file.
081bf30e 52The contents of this file are sent to the inferior Octave process on
28d16ed3
AS
53startup."
54 :type '(choice (const :tag "None" nil)
55 file)
56 :group 'octave-inferior)
081bf30e 57
3a5a5d36 58(defcustom inferior-octave-startup-args nil
e2536e57 59 "List of command line arguments for the inferior Octave process.
081bf30e 60For example, for suppressing the startup message and using `traditional'
28d16ed3
AS
61mode, set this to (\"-q\" \"--traditional\")."
62 :type '(repeat string)
63 :group 'octave-inferior)
081bf30e 64
e2536e57
SM
65(defvar inferior-octave-mode-map
66 (let ((map (make-sparse-keymap)))
67 (set-keymap-parent map comint-mode-map)
081bf30e
RS
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)
e2536e57
SM
74 map)
75 "Keymap used in Inferior Octave mode.")
081bf30e 76
e2536e57 77(defvar inferior-octave-mode-syntax-table
081bf30e
RS
78 (let ((table (make-syntax-table)))
79 (modify-syntax-entry ?\` "w" table)
80 (modify-syntax-entry ?\# "<" table)
81 (modify-syntax-entry ?\n ">" table)
e2536e57
SM
82 table)
83 "Syntax table in use in inferior-octave-mode buffers.")
081bf30e 84
28d16ed3
AS
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)
081bf30e
RS
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
9d245da5
MB
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.
102The prompt is assumed to be any text at the beginning of the line matching
103the regular expression `comint-prompt-regexp', a buffer local variable."
104 (save-excursion (comint-bol nil) (point))))
105
106
081bf30e
RS
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-dynamic-complete-functions
a1506d29 117 '(inferior-octave-complete comint-dynamic-complete-filename)
081bf30e
RS
118 "List of functions called to perform completion for inferior Octave.
119This variable is used to initialize `comint-dynamic-complete-functions'
120in the Inferior Octave buffer.")
121
122(defun inferior-octave-mode ()
123 "Major mode for interacting with an inferior Octave process.
124Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
125buffer.
126
127Entry to this mode successively runs the hooks `comint-mode-hook' and
128`inferior-octave-mode-hook'."
129 (interactive)
cb1b44b0 130 (delay-mode-hooks (comint-mode))
081bf30e
RS
131 (setq comint-prompt-regexp inferior-octave-prompt
132 major-mode 'inferior-octave-mode
133 mode-name "Inferior Octave"
134 mode-line-process '(":%s")
135 local-abbrev-table octave-abbrev-table)
136 (use-local-map inferior-octave-mode-map)
137 (set-syntax-table inferior-octave-mode-syntax-table)
138
a1506d29 139 (make-local-variable 'comment-start)
081bf30e
RS
140 (setq comment-start octave-comment-start)
141 (make-local-variable 'comment-end)
142 (setq comment-end "")
143 (make-local-variable 'comment-column)
a1506d29 144 (setq comment-column 32)
081bf30e
RS
145 (make-local-variable 'comment-start-skip)
146 (setq comment-start-skip octave-comment-start-skip)
147
148 (make-local-variable 'font-lock-defaults)
149 (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))
150
151 (setq comint-input-ring-file-name
152 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
153 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024)
e2536e57
SM
154 comint-input-filter-functions '(inferior-octave-directory-tracker))
155 (set (make-local-variable 'comint-dynamic-complete-functions)
156 inferior-octave-dynamic-complete-functions)
081bf30e
RS
157 (comint-read-input-ring t)
158
9a969196 159 (run-mode-hooks 'inferior-octave-mode-hook))
081bf30e
RS
160
161;;;###autoload
162(defun inferior-octave (&optional arg)
163 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
164This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
165
166Unless ARG is non-nil, switches to this buffer.
167
168The elements of the list `inferior-octave-startup-args' are sent as
169command line arguments to the inferior Octave process on startup.
170
171Additional commands to be executed on startup can be provided either in
172the file specified by `inferior-octave-startup-file' or by the default
173startup file, `~/.emacs-octave'."
174 (interactive "P")
175 (let ((buffer inferior-octave-buffer))
176 (get-buffer-create buffer)
177 (if (comint-check-proc buffer)
178 ()
179 (save-excursion
180 (set-buffer buffer)
181 (comint-mode)
182 (inferior-octave-startup)
183 (inferior-octave-mode)))
184 (if (not arg)
185 (pop-to-buffer buffer))))
186
187;;;###autoload
188(defalias 'run-octave 'inferior-octave)
189
190(defun inferior-octave-startup ()
191 "Start an inferior Octave process."
192 (let ((proc (comint-exec-1
193 (substring inferior-octave-buffer 1 -1)
194 inferior-octave-buffer
195 inferior-octave-program
3a5a5d36
SE
196 (append (list "-i" "--no-line-editing")
197 inferior-octave-startup-args))))
081bf30e
RS
198 (set-process-filter proc 'inferior-octave-output-digest)
199 (setq comint-ptyp process-connection-type
200 inferior-octave-process proc
201 inferior-octave-output-list nil
202 inferior-octave-output-string nil
203 inferior-octave-receive-in-progress t)
204
205 ;; This may look complicated ... However, we need to make sure that
206 ;; we additional startup code only AFTER Octave is ready (otherwise,
207 ;; output may be mixed up). Hence, we need to digest the Octave
208 ;; output to see when it issues a prompt.
209 (while inferior-octave-receive-in-progress
210 (accept-process-output inferior-octave-process))
211 (goto-char (point-max))
212 (set-marker (process-mark proc) (point))
213 (insert-before-markers
214 (concat
215 (if (not (bobp)) "\f\n")
216 (if inferior-octave-output-list
217 (concat (mapconcat
218 'identity inferior-octave-output-list "\n")
219 "\n"))))
107dd21b
EZ
220
221 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
222 ;; means trouble.
223 (inferior-octave-send-list-and-digest (list "PS2\n"))
224 (if (string-match "^PS2 = *$" (car inferior-octave-output-list))
225 (inferior-octave-send-list-and-digest (list "PS2 = \"> \"\n")))
226
081bf30e
RS
227 ;; O.k., now we are ready for the Inferior Octave startup commands.
228 (let* (commands
229 (program (file-name-nondirectory inferior-octave-program))
230 (file (or inferior-octave-startup-file
231 (concat "~/.emacs-" program))))
232 (setq commands
233 (list "page_screen_output = 0;\n"
234 (if (not (string-equal
a1506d29 235 inferior-octave-output-string ">> "))
081bf30e
RS
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)
4ca7c468
EZ
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)))
081bf30e
RS
260
261\f
262(defun inferior-octave-complete ()
263 "Perform completion on the Octave symbol preceding point.
264This is implemented using the Octave command `completion_matches' which
265is NOT available with versions of Octave prior to 2.0."
266 (interactive)
267 (let* ((end (point))
502166d2
MB
268 (command
269 (save-excursion
270 (skip-syntax-backward "w_" (comint-line-beginning-position))
271 (buffer-substring-no-properties (point) end)))
e2536e57 272 (proc (get-buffer-process inferior-octave-buffer)))
081bf30e
RS
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 ()
e2536e57 298 "List the buffer's input history in a help buffer."
081bf30e
RS
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.
326If 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.
335Ring Emacs bell if process output starts with an ASCII bell, and pass
336the 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.
341Save all output between newlines into `inferior-octave-output-list', and
342the 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.
355The elements of LIST have to be strings and are sent one by one. All
356output 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.
374Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
df287ddb
SE
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))))))
081bf30e
RS
380
381(defun inferior-octave-resync-dirs ()
382 "Resync the buffer's idea of the current directory.
383This command queries the inferior Octave process about its current
384directory and makes this the current buffer's default directory."
385 (interactive)
386 (inferior-octave-send-list-and-digest '("pwd\n"))
387 (cd (car inferior-octave-output-list)))
388
15c2d736
KH
389;;; provide ourself
390
391(provide 'octave-inf)
392
e2536e57 393;; arch-tag: bdce0395-24d1-4bb4-bfba-6fb1eeb1a660
e96a83c8 394;;; octave-inf.el ends here