Copyright up-date.
[bpt/emacs.git] / lisp / progmodes / octave-inf.el
CommitLineData
092af6d8 1;;; octave-inf.el --- running Octave as an inferior Emacs process
081bf30e 2
092af6d8 3;; Copyright (C) 1997 Free Software Foundation, Inc.
081bf30e
RS
4
5;; Author: Kurt Hornik <Kurt.Hornik@ci.tuwien.ac.at>
6;; Author: John Eaton <jwe@bevo.che.wisc.edu>
7;; Maintainer: Kurt Hornik <Kurt.Hornik@ci.tuwien.ac.at>
8;; Keywords: languages
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
081bf30e
RS
27;;; Code:
28
f7bbab75 29(require 'octave-mod)
081bf30e
RS
30(require 'comint)
31
28d16ed3
AS
32(defgroup octave-inferior nil
33 "Running Octave as an inferior Emacs process."
34 :group 'octave)
081bf30e 35
28d16ed3
AS
36(defcustom inferior-octave-program "octave"
37 "*Program invoked by `inferior-octave'."
38 :type 'string
39 :group 'octave-inferior)
40
41(defcustom inferior-octave-prompt
9eb580d2 42 "\\(^octave\\(\\|.bin\\)\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
28d16ed3
AS
43 "*Regexp to match prompts for the inferior Octave process."
44 :type 'regexp
45 :group 'octave-inferior)
081bf30e 46
28d16ed3 47(defcustom inferior-octave-startup-file nil
081bf30e
RS
48 "*Name of the inferior Octave startup file.
49The contents of this file are sent to the inferior Octave process on
28d16ed3
AS
50startup."
51 :type '(choice (const :tag "None" nil)
52 file)
53 :group 'octave-inferior)
081bf30e 54
3a5a5d36 55(defcustom inferior-octave-startup-args nil
081bf30e
RS
56 "*List of command line arguments for the inferior Octave process.
57For example, for suppressing the startup message and using `traditional'
28d16ed3
AS
58mode, set this to (\"-q\" \"--traditional\")."
59 :type '(repeat string)
60 :group 'octave-inferior)
081bf30e
RS
61
62(defvar inferior-octave-mode-map nil
63 "Keymap used in Inferior Octave mode.")
64(if inferior-octave-mode-map
65 ()
66 (let ((map (copy-keymap 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 (setq inferior-octave-mode-map map)))
74
75(defvar inferior-octave-mode-syntax-table nil
76 "Syntax table in use in inferior-octave-mode buffers.")
77(if inferior-octave-mode-syntax-table
78 ()
79 (let ((table (make-syntax-table)))
80 (modify-syntax-entry ?\` "w" table)
81 (modify-syntax-entry ?\# "<" table)
82 (modify-syntax-entry ?\n ">" table)
83 (setq inferior-octave-mode-syntax-table table)))
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
96(defvar inferior-octave-output-list nil)
97(defvar inferior-octave-output-string nil)
98(defvar inferior-octave-receive-in-progress nil)
99
100(defvar inferior-octave-startup-hook nil)
101
102(defvar inferior-octave-complete-impossible nil
103 "Non-nil means that `inferior-octave-complete' is impossible.")
104
105(defvar inferior-octave-dynamic-complete-functions
106 '(inferior-octave-complete comint-dynamic-complete-filename)
107 "List of functions called to perform completion for inferior Octave.
108This variable is used to initialize `comint-dynamic-complete-functions'
109in the Inferior Octave buffer.")
110
111(defun inferior-octave-mode ()
112 "Major mode for interacting with an inferior Octave process.
113Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
114buffer.
115
116Entry to this mode successively runs the hooks `comint-mode-hook' and
117`inferior-octave-mode-hook'."
118 (interactive)
119 (comint-mode)
120 (setq comint-prompt-regexp inferior-octave-prompt
121 major-mode 'inferior-octave-mode
122 mode-name "Inferior Octave"
123 mode-line-process '(":%s")
124 local-abbrev-table octave-abbrev-table)
125 (use-local-map inferior-octave-mode-map)
126 (set-syntax-table inferior-octave-mode-syntax-table)
127
128 (make-local-variable 'comment-start)
129 (setq comment-start octave-comment-start)
130 (make-local-variable 'comment-end)
131 (setq comment-end "")
132 (make-local-variable 'comment-column)
133 (setq comment-column 32)
134 (make-local-variable 'comment-start-skip)
135 (setq comment-start-skip octave-comment-start-skip)
136
137 (make-local-variable 'font-lock-defaults)
138 (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))
139
140 (setq comint-input-ring-file-name
141 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
142 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024)
143 comint-input-filter-functions '(inferior-octave-directory-tracker)
144 comint-dynamic-complete-functions
145 inferior-octave-dynamic-complete-functions)
146 (comint-read-input-ring t)
147
148 (run-hooks 'inferior-octave-mode-hook))
149
150;;;###autoload
151(defun inferior-octave (&optional arg)
152 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
153This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
154
155Unless ARG is non-nil, switches to this buffer.
156
157The elements of the list `inferior-octave-startup-args' are sent as
158command line arguments to the inferior Octave process on startup.
159
160Additional commands to be executed on startup can be provided either in
161the file specified by `inferior-octave-startup-file' or by the default
162startup file, `~/.emacs-octave'."
163 (interactive "P")
164 (let ((buffer inferior-octave-buffer))
165 (get-buffer-create buffer)
166 (if (comint-check-proc buffer)
167 ()
168 (save-excursion
169 (set-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
3a5a5d36
SE
185 (append (list "-i" "--no-line-editing")
186 inferior-octave-startup-args))))
081bf30e
RS
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 ;; O.k., now we are ready for the Inferior Octave startup commands.
210 (let* (commands
211 (program (file-name-nondirectory inferior-octave-program))
212 (file (or inferior-octave-startup-file
213 (concat "~/.emacs-" program))))
214 (setq commands
215 (list "page_screen_output = 0;\n"
216 (if (not (string-equal
217 inferior-octave-output-string ">> "))
218 "PS1=\"\\\\s> \";\n")
219 (if (file-exists-p file)
220 (format "source (\"%s\");\n" file))))
221 (inferior-octave-send-list-and-digest commands))
222 (insert-before-markers
223 (concat
224 (if inferior-octave-output-list
225 (concat (mapconcat
226 'identity inferior-octave-output-list "\n")
227 "\n"))
228 inferior-octave-output-string))
229 ;; Next, we check whether Octave supports `completion_matches' ...
230 (inferior-octave-send-list-and-digest
231 (list "exist \"completion_matches\"\n"))
232 (setq inferior-octave-complete-impossible
233 (not (string-match "5$" (car inferior-octave-output-list))))
234
235 ;; And finally, everything is back to normal.
236 (set-process-filter proc 'inferior-octave-output-filter)
237 (run-hooks 'inferior-octave-startup-hook)))
238
239\f
240(defun inferior-octave-complete ()
241 "Perform completion on the Octave symbol preceding point.
242This is implemented using the Octave command `completion_matches' which
243is NOT available with versions of Octave prior to 2.0."
244 (interactive)
245 (let* ((end (point))
502166d2
MB
246 (command
247 (save-excursion
248 (skip-syntax-backward "w_" (comint-line-beginning-position))
249 (buffer-substring-no-properties (point) end)))
081bf30e
RS
250 (proc (get-buffer-process inferior-octave-buffer))
251 (filter (process-filter proc)))
252 (cond (inferior-octave-complete-impossible
253 (error (concat
254 "Your Octave does not have `completion_matches'. "
255 "Please upgrade to version 2.X.")))
256 ((string-equal command "")
257 (message "Cannot complete an empty string"))
258 (t
259 (inferior-octave-send-list-and-digest
260 (list (concat "completion_matches (\"" command "\");\n")))
261 ;; Sort the list
262 (setq inferior-octave-output-list
263 (sort inferior-octave-output-list 'string-lessp))
264 ;; Remove duplicates
265 (let* ((x inferior-octave-output-list)
266 (y (cdr x)))
267 (while y
268 (if (string-equal (car x) (car y))
269 (setcdr x (setq y (cdr y)))
270 (setq x y
271 y (cdr y)))))
272 ;; And let comint handle the rest
273 (comint-dynamic-simple-complete
274 command inferior-octave-output-list)))))
275
276(defun inferior-octave-dynamic-list-input-ring ()
277 "List the buffer's input history in a help buffer"
278 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
279 ;; "completion" by "history reference" ...
280 (interactive)
281 (if (or (not (ring-p comint-input-ring))
282 (ring-empty-p comint-input-ring))
283 (message "No history")
284 (let ((history nil)
285 (history-buffer " *Input History*")
286 (index (1- (ring-length comint-input-ring)))
287 (conf (current-window-configuration)))
288 ;; We have to build up a list ourselves from the ring vector.
289 (while (>= index 0)
290 (setq history (cons (ring-ref comint-input-ring index) history)
291 index (1- index)))
292 ;; Change "completion" to "history reference"
293 ;; to make the display accurate.
294 (with-output-to-temp-buffer history-buffer
295 (display-completion-list history)
296 (set-buffer history-buffer))
297 (message "Hit space to flush")
298 (let ((ch (read-event)))
299 (if (eq ch ?\ )
300 (set-window-configuration conf)
301 (setq unread-command-events (list ch)))))))
302
303(defun inferior-octave-strip-ctrl-g (string)
304 "Strip leading `^G' character.
305If STRING starts with a `^G', ring the bell and strip it."
306 (if (string-match "^\a" string)
307 (progn
308 (ding)
309 (setq string (substring string 1))))
310 string)
311
312(defun inferior-octave-output-filter (proc string)
313 "Standard output filter for the inferior Octave process.
314Ring Emacs bell if process output starts with an ASCII bell, and pass
315the rest to `comint-output-filter'."
316 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
317
318(defun inferior-octave-output-digest (proc string)
319 "Special output filter for the inferior Octave process.
320Save all output between newlines into `inferior-octave-output-list', and
321the rest to `inferior-octave-output-string'."
322 (setq string (concat inferior-octave-output-string string))
323 (while (string-match "\n" string)
324 (setq inferior-octave-output-list
325 (append inferior-octave-output-list
326 (list (substring string 0 (match-beginning 0))))
327 string (substring string (match-end 0))))
328 (if (string-match inferior-octave-prompt string)
329 (setq inferior-octave-receive-in-progress nil))
330 (setq inferior-octave-output-string string))
331
332(defun inferior-octave-send-list-and-digest (list)
333 "Send LIST to the inferior Octave process and digest the output.
334The elements of LIST have to be strings and are sent one by one. All
335output is passed to the filter `inferior-octave-output-digest'."
336 (let* ((proc inferior-octave-process)
337 (filter (process-filter proc))
338 string)
339 (set-process-filter proc 'inferior-octave-output-digest)
340 (setq inferior-octave-output-list nil)
341 (unwind-protect
342 (while (setq string (car list))
343 (setq inferior-octave-output-string nil
344 inferior-octave-receive-in-progress t)
345 (comint-send-string proc string)
346 (while inferior-octave-receive-in-progress
347 (accept-process-output proc))
348 (setq list (cdr list)))
349 (set-process-filter proc filter))))
350
351(defun inferior-octave-directory-tracker (string)
352 "Tracks `cd' commands issued to the inferior Octave process.
353Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
df287ddb
SE
354 (cond
355 ((string-match "^[ \t]*cd[ \t;]*$" string)
356 (cd "~"))
357 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
358 (cd (substring string (match-beginning 1) (match-end 1))))))
081bf30e
RS
359
360(defun inferior-octave-resync-dirs ()
361 "Resync the buffer's idea of the current directory.
362This command queries the inferior Octave process about its current
363directory and makes this the current buffer's default directory."
364 (interactive)
365 (inferior-octave-send-list-and-digest '("pwd\n"))
366 (cd (car inferior-octave-output-list)))
367
15c2d736
KH
368;;; provide ourself
369
370(provide 'octave-inf)
371
e96a83c8 372;;; octave-inf.el ends here