Some fixes to follow coding conventions.
[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
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
AS
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
9eb580d2 44 "\\(^octave\\(\\|.bin\\)\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
28d16ed3
AS
45 "*Regexp to match prompts for the inferior Octave process."
46 :type 'regexp
47 :group 'octave-inferior)
081bf30e 48
28d16ed3 49(defcustom inferior-octave-startup-file nil
081bf30e
RS
50 "*Name of the inferior Octave startup file.
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
081bf30e
RS
58 "*List of command line arguments for the inferior Octave process.
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
RS
63
64(defvar inferior-octave-mode-map nil
65 "Keymap used in Inferior Octave mode.")
66(if inferior-octave-mode-map
67 ()
68 (let ((map (copy-keymap comint-mode-map)))
69 (define-key map "\t" 'comint-dynamic-complete)
70 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
71 (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
72 (define-key map [menu-bar inout list-history]
73 '("List Input History" . inferior-octave-dynamic-list-input-ring))
74 (define-key map "\C-c\C-h" 'octave-help)
75 (setq inferior-octave-mode-map map)))
76
77(defvar inferior-octave-mode-syntax-table nil
78 "Syntax table in use in inferior-octave-mode buffers.")
79(if inferior-octave-mode-syntax-table
80 ()
81 (let ((table (make-syntax-table)))
82 (modify-syntax-entry ?\` "w" table)
83 (modify-syntax-entry ?\# "<" table)
84 (modify-syntax-entry ?\n ">" table)
85 (setq inferior-octave-mode-syntax-table table)))
86
28d16ed3
AS
87(defcustom inferior-octave-mode-hook nil
88 "*Hook to be run when Inferior Octave mode is started."
89 :type 'hook
90 :group 'octave-inferior)
081bf30e
RS
91
92(defvar inferior-octave-font-lock-keywords
93 (list
94 (cons inferior-octave-prompt 'font-lock-type-face))
95 ;; Could certainly do more font locking in inferior Octave ...
96 "Additional expressions to highlight in Inferior Octave mode.")
97
9d245da5
MB
98
99;;; Compatibility functions
100(if (not (fboundp 'comint-line-beginning-position))
101 ;; comint-line-beginning-position is defined in Emacs 21
102 (defun comint-line-beginning-position ()
103 "Returns the buffer position of the beginning of the line, after any prompt.
104The prompt is assumed to be any text at the beginning of the line matching
105the regular expression `comint-prompt-regexp', a buffer local variable."
106 (save-excursion (comint-bol nil) (point))))
107
108
081bf30e
RS
109(defvar inferior-octave-output-list nil)
110(defvar inferior-octave-output-string nil)
111(defvar inferior-octave-receive-in-progress nil)
112
113(defvar inferior-octave-startup-hook nil)
114
115(defvar inferior-octave-complete-impossible nil
116 "Non-nil means that `inferior-octave-complete' is impossible.")
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.
121This variable is used to initialize `comint-dynamic-complete-functions'
122in the Inferior Octave buffer.")
123
124(defun inferior-octave-mode ()
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'."
131 (interactive)
132 (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 comint-input-filter-functions '(inferior-octave-directory-tracker)
157 comint-dynamic-complete-functions
158 inferior-octave-dynamic-complete-functions)
159 (comint-read-input-ring t)
160
161 (run-hooks 'inferior-octave-mode-hook))
162
163;;;###autoload
164(defun inferior-octave (&optional arg)
165 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
166This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
167
168Unless ARG is non-nil, switches to this buffer.
169
170The elements of the list `inferior-octave-startup-args' are sent as
171command line arguments to the inferior Octave process on startup.
172
173Additional commands to be executed on startup can be provided either in
174the file specified by `inferior-octave-startup-file' or by the default
175startup file, `~/.emacs-octave'."
176 (interactive "P")
177 (let ((buffer inferior-octave-buffer))
178 (get-buffer-create buffer)
179 (if (comint-check-proc buffer)
180 ()
181 (save-excursion
182 (set-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
3a5a5d36
SE
198 (append (list "-i" "--no-line-editing")
199 inferior-octave-startup-args))))
081bf30e
RS
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 ;; O.k., now we are ready for the Inferior Octave startup commands.
223 (let* (commands
224 (program (file-name-nondirectory inferior-octave-program))
225 (file (or inferior-octave-startup-file
226 (concat "~/.emacs-" program))))
227 (setq commands
228 (list "page_screen_output = 0;\n"
229 (if (not (string-equal
230 inferior-octave-output-string ">> "))
231 "PS1=\"\\\\s> \";\n")
232 (if (file-exists-p file)
233 (format "source (\"%s\");\n" file))))
234 (inferior-octave-send-list-and-digest commands))
235 (insert-before-markers
236 (concat
237 (if inferior-octave-output-list
238 (concat (mapconcat
239 'identity inferior-octave-output-list "\n")
240 "\n"))
241 inferior-octave-output-string))
242 ;; Next, we check whether Octave supports `completion_matches' ...
243 (inferior-octave-send-list-and-digest
244 (list "exist \"completion_matches\"\n"))
245 (setq inferior-octave-complete-impossible
246 (not (string-match "5$" (car inferior-octave-output-list))))
247
248 ;; And finally, everything is back to normal.
249 (set-process-filter proc 'inferior-octave-output-filter)
250 (run-hooks 'inferior-octave-startup-hook)))
251
252\f
253(defun inferior-octave-complete ()
254 "Perform completion on the Octave symbol preceding point.
255This is implemented using the Octave command `completion_matches' which
256is NOT available with versions of Octave prior to 2.0."
257 (interactive)
258 (let* ((end (point))
502166d2
MB
259 (command
260 (save-excursion
261 (skip-syntax-backward "w_" (comint-line-beginning-position))
262 (buffer-substring-no-properties (point) end)))
081bf30e
RS
263 (proc (get-buffer-process inferior-octave-buffer))
264 (filter (process-filter proc)))
265 (cond (inferior-octave-complete-impossible
266 (error (concat
267 "Your Octave does not have `completion_matches'. "
268 "Please upgrade to version 2.X.")))
269 ((string-equal command "")
270 (message "Cannot complete an empty string"))
271 (t
272 (inferior-octave-send-list-and-digest
273 (list (concat "completion_matches (\"" command "\");\n")))
274 ;; Sort the list
275 (setq inferior-octave-output-list
276 (sort inferior-octave-output-list 'string-lessp))
277 ;; Remove duplicates
278 (let* ((x inferior-octave-output-list)
279 (y (cdr x)))
280 (while y
281 (if (string-equal (car x) (car y))
282 (setcdr x (setq y (cdr y)))
283 (setq x y
284 y (cdr y)))))
285 ;; And let comint handle the rest
286 (comint-dynamic-simple-complete
287 command inferior-octave-output-list)))))
288
289(defun inferior-octave-dynamic-list-input-ring ()
290 "List the buffer's input history in a help buffer"
291 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
292 ;; "completion" by "history reference" ...
293 (interactive)
294 (if (or (not (ring-p comint-input-ring))
295 (ring-empty-p comint-input-ring))
296 (message "No history")
297 (let ((history nil)
298 (history-buffer " *Input History*")
299 (index (1- (ring-length comint-input-ring)))
300 (conf (current-window-configuration)))
301 ;; We have to build up a list ourselves from the ring vector.
302 (while (>= index 0)
303 (setq history (cons (ring-ref comint-input-ring index) history)
304 index (1- index)))
305 ;; Change "completion" to "history reference"
306 ;; to make the display accurate.
307 (with-output-to-temp-buffer history-buffer
308 (display-completion-list history)
309 (set-buffer history-buffer))
310 (message "Hit space to flush")
311 (let ((ch (read-event)))
312 (if (eq ch ?\ )
313 (set-window-configuration conf)
314 (setq unread-command-events (list ch)))))))
315
316(defun inferior-octave-strip-ctrl-g (string)
317 "Strip leading `^G' character.
318If STRING starts with a `^G', ring the bell and strip it."
319 (if (string-match "^\a" string)
320 (progn
321 (ding)
322 (setq string (substring string 1))))
323 string)
324
325(defun inferior-octave-output-filter (proc string)
326 "Standard output filter for the inferior Octave process.
327Ring Emacs bell if process output starts with an ASCII bell, and pass
328the rest to `comint-output-filter'."
329 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
330
331(defun inferior-octave-output-digest (proc string)
332 "Special output filter for the inferior Octave process.
333Save all output between newlines into `inferior-octave-output-list', and
334the rest to `inferior-octave-output-string'."
335 (setq string (concat inferior-octave-output-string string))
336 (while (string-match "\n" string)
337 (setq inferior-octave-output-list
338 (append inferior-octave-output-list
339 (list (substring string 0 (match-beginning 0))))
340 string (substring string (match-end 0))))
341 (if (string-match inferior-octave-prompt string)
342 (setq inferior-octave-receive-in-progress nil))
343 (setq inferior-octave-output-string string))
344
345(defun inferior-octave-send-list-and-digest (list)
346 "Send LIST to the inferior Octave process and digest the output.
347The elements of LIST have to be strings and are sent one by one. All
348output is passed to the filter `inferior-octave-output-digest'."
349 (let* ((proc inferior-octave-process)
350 (filter (process-filter proc))
351 string)
352 (set-process-filter proc 'inferior-octave-output-digest)
353 (setq inferior-octave-output-list nil)
354 (unwind-protect
355 (while (setq string (car list))
356 (setq inferior-octave-output-string nil
357 inferior-octave-receive-in-progress t)
358 (comint-send-string proc string)
359 (while inferior-octave-receive-in-progress
360 (accept-process-output proc))
361 (setq list (cdr list)))
362 (set-process-filter proc filter))))
363
364(defun inferior-octave-directory-tracker (string)
365 "Tracks `cd' commands issued to the inferior Octave process.
366Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
df287ddb
SE
367 (cond
368 ((string-match "^[ \t]*cd[ \t;]*$" string)
369 (cd "~"))
370 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
371 (cd (substring string (match-beginning 1) (match-end 1))))))
081bf30e
RS
372
373(defun inferior-octave-resync-dirs ()
374 "Resync the buffer's idea of the current directory.
375This command queries the inferior Octave process about its current
376directory and makes this the current buffer's default directory."
377 (interactive)
378 (inferior-octave-send-list-and-digest '("pwd\n"))
379 (cd (car inferior-octave-output-list)))
380
15c2d736
KH
381;;; provide ourself
382
383(provide 'octave-inf)
384
e96a83c8 385;;; octave-inf.el ends here