(propertized-buffer-identification): Use face
[bpt/emacs.git] / lisp / progmodes / octave-inf.el
CommitLineData
092af6d8 1;;; octave-inf.el --- running Octave as an inferior Emacs process
081bf30e 2
034babe1
NR
3;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005
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
AS
39(defcustom inferior-octave-program "octave"
40 "*Program invoked by `inferior-octave'."
41 :type 'string
42 :group 'octave-inferior)
43
44(defcustom inferior-octave-prompt
b2fc0976 45 "\\(^octave\\(\\|.bin\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
28d16ed3
AS
46 "*Regexp to match prompts for the inferior Octave process."
47 :type 'regexp
48 :group 'octave-inferior)
081bf30e 49
28d16ed3 50(defcustom inferior-octave-startup-file nil
081bf30e
RS
51 "*Name of the inferior Octave startup file.
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
081bf30e
RS
59 "*List of command line arguments for the inferior Octave process.
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
RS
64
65(defvar inferior-octave-mode-map nil
66 "Keymap used in Inferior Octave mode.")
67(if inferior-octave-mode-map
68 ()
69 (let ((map (copy-keymap comint-mode-map)))
70 (define-key map "\t" 'comint-dynamic-complete)
71 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
72 (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
73 (define-key map [menu-bar inout list-history]
74 '("List Input History" . inferior-octave-dynamic-list-input-ring))
75 (define-key map "\C-c\C-h" 'octave-help)
76 (setq inferior-octave-mode-map map)))
77
78(defvar inferior-octave-mode-syntax-table nil
79 "Syntax table in use in inferior-octave-mode buffers.")
80(if inferior-octave-mode-syntax-table
81 ()
82 (let ((table (make-syntax-table)))
83 (modify-syntax-entry ?\` "w" table)
84 (modify-syntax-entry ?\# "<" table)
85 (modify-syntax-entry ?\n ">" table)
86 (setq inferior-octave-mode-syntax-table table)))
87
28d16ed3
AS
88(defcustom inferior-octave-mode-hook nil
89 "*Hook to be run when Inferior Octave mode is started."
90 :type 'hook
91 :group 'octave-inferior)
081bf30e
RS
92
93(defvar inferior-octave-font-lock-keywords
94 (list
95 (cons inferior-octave-prompt 'font-lock-type-face))
96 ;; Could certainly do more font locking in inferior Octave ...
97 "Additional expressions to highlight in Inferior Octave mode.")
98
9d245da5
MB
99
100;;; Compatibility functions
101(if (not (fboundp 'comint-line-beginning-position))
102 ;; comint-line-beginning-position is defined in Emacs 21
103 (defun comint-line-beginning-position ()
104 "Returns the buffer position of the beginning of the line, after any prompt.
105The prompt is assumed to be any text at the beginning of the line matching
106the regular expression `comint-prompt-regexp', a buffer local variable."
107 (save-excursion (comint-bol nil) (point))))
108
109
081bf30e
RS
110(defvar inferior-octave-output-list nil)
111(defvar inferior-octave-output-string nil)
112(defvar inferior-octave-receive-in-progress nil)
113
114(defvar inferior-octave-startup-hook nil)
115
116(defvar inferior-octave-complete-impossible nil
117 "Non-nil means that `inferior-octave-complete' is impossible.")
118
119(defvar inferior-octave-dynamic-complete-functions
a1506d29 120 '(inferior-octave-complete comint-dynamic-complete-filename)
081bf30e
RS
121 "List of functions called to perform completion for inferior Octave.
122This variable is used to initialize `comint-dynamic-complete-functions'
123in the Inferior Octave buffer.")
124
125(defun inferior-octave-mode ()
126 "Major mode for interacting with an inferior Octave process.
127Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
128buffer.
129
130Entry to this mode successively runs the hooks `comint-mode-hook' and
131`inferior-octave-mode-hook'."
132 (interactive)
cb1b44b0 133 (delay-mode-hooks (comint-mode))
081bf30e
RS
134 (setq comint-prompt-regexp inferior-octave-prompt
135 major-mode 'inferior-octave-mode
136 mode-name "Inferior Octave"
137 mode-line-process '(":%s")
138 local-abbrev-table octave-abbrev-table)
139 (use-local-map inferior-octave-mode-map)
140 (set-syntax-table inferior-octave-mode-syntax-table)
141
a1506d29 142 (make-local-variable 'comment-start)
081bf30e
RS
143 (setq comment-start octave-comment-start)
144 (make-local-variable 'comment-end)
145 (setq comment-end "")
146 (make-local-variable 'comment-column)
a1506d29 147 (setq comment-column 32)
081bf30e
RS
148 (make-local-variable 'comment-start-skip)
149 (setq comment-start-skip octave-comment-start-skip)
150
151 (make-local-variable 'font-lock-defaults)
152 (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))
153
154 (setq comint-input-ring-file-name
155 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
156 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024)
157 comint-input-filter-functions '(inferior-octave-directory-tracker)
158 comint-dynamic-complete-functions
159 inferior-octave-dynamic-complete-functions)
160 (comint-read-input-ring t)
161
9a969196 162 (run-mode-hooks 'inferior-octave-mode-hook))
081bf30e
RS
163
164;;;###autoload
165(defun inferior-octave (&optional arg)
166 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
167This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
168
169Unless ARG is non-nil, switches to this buffer.
170
171The elements of the list `inferior-octave-startup-args' are sent as
172command line arguments to the inferior Octave process on startup.
173
174Additional commands to be executed on startup can be provided either in
175the file specified by `inferior-octave-startup-file' or by the default
176startup 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 (save-excursion
183 (set-buffer buffer)
184 (comint-mode)
185 (inferior-octave-startup)
186 (inferior-octave-mode)))
187 (if (not arg)
188 (pop-to-buffer buffer))))
189
190;;;###autoload
191(defalias 'run-octave 'inferior-octave)
192
193(defun inferior-octave-startup ()
194 "Start an inferior Octave process."
195 (let ((proc (comint-exec-1
196 (substring inferior-octave-buffer 1 -1)
197 inferior-octave-buffer
198 inferior-octave-program
3a5a5d36
SE
199 (append (list "-i" "--no-line-editing")
200 inferior-octave-startup-args))))
081bf30e
RS
201 (set-process-filter proc 'inferior-octave-output-digest)
202 (setq comint-ptyp process-connection-type
203 inferior-octave-process proc
204 inferior-octave-output-list nil
205 inferior-octave-output-string nil
206 inferior-octave-receive-in-progress t)
207
208 ;; This may look complicated ... However, we need to make sure that
209 ;; we additional startup code only AFTER Octave is ready (otherwise,
210 ;; output may be mixed up). Hence, we need to digest the Octave
211 ;; output to see when it issues a prompt.
212 (while inferior-octave-receive-in-progress
213 (accept-process-output inferior-octave-process))
214 (goto-char (point-max))
215 (set-marker (process-mark proc) (point))
216 (insert-before-markers
217 (concat
218 (if (not (bobp)) "\f\n")
219 (if inferior-octave-output-list
220 (concat (mapconcat
221 'identity inferior-octave-output-list "\n")
222 "\n"))))
107dd21b
EZ
223
224 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
225 ;; means trouble.
226 (inferior-octave-send-list-and-digest (list "PS2\n"))
227 (if (string-match "^PS2 = *$" (car inferior-octave-output-list))
228 (inferior-octave-send-list-and-digest (list "PS2 = \"> \"\n")))
229
081bf30e
RS
230 ;; O.k., now we are ready for the Inferior Octave startup commands.
231 (let* (commands
232 (program (file-name-nondirectory inferior-octave-program))
233 (file (or inferior-octave-startup-file
234 (concat "~/.emacs-" program))))
235 (setq commands
236 (list "page_screen_output = 0;\n"
237 (if (not (string-equal
a1506d29 238 inferior-octave-output-string ">> "))
081bf30e
RS
239 "PS1=\"\\\\s> \";\n")
240 (if (file-exists-p file)
241 (format "source (\"%s\");\n" file))))
242 (inferior-octave-send-list-and-digest commands))
243 (insert-before-markers
244 (concat
245 (if inferior-octave-output-list
246 (concat (mapconcat
247 'identity inferior-octave-output-list "\n")
248 "\n"))
249 inferior-octave-output-string))
250 ;; Next, we check whether Octave supports `completion_matches' ...
251 (inferior-octave-send-list-and-digest
252 (list "exist \"completion_matches\"\n"))
253 (setq inferior-octave-complete-impossible
254 (not (string-match "5$" (car inferior-octave-output-list))))
255
256 ;; And finally, everything is back to normal.
257 (set-process-filter proc 'inferior-octave-output-filter)
4ca7c468
EZ
258 (run-hooks 'inferior-octave-startup-hook)
259 (run-hooks 'inferior-octave-startup-hook)
260 ;; Just in case, to be sure a cd in the startup file
261 ;; won't have detrimental effects.
262 (inferior-octave-resync-dirs)))
081bf30e
RS
263
264\f
265(defun inferior-octave-complete ()
266 "Perform completion on the Octave symbol preceding point.
267This is implemented using the Octave command `completion_matches' which
268is NOT available with versions of Octave prior to 2.0."
269 (interactive)
270 (let* ((end (point))
502166d2
MB
271 (command
272 (save-excursion
273 (skip-syntax-backward "w_" (comint-line-beginning-position))
274 (buffer-substring-no-properties (point) end)))
081bf30e
RS
275 (proc (get-buffer-process inferior-octave-buffer))
276 (filter (process-filter proc)))
277 (cond (inferior-octave-complete-impossible
278 (error (concat
279 "Your Octave does not have `completion_matches'. "
280 "Please upgrade to version 2.X.")))
281 ((string-equal command "")
282 (message "Cannot complete an empty string"))
283 (t
284 (inferior-octave-send-list-and-digest
285 (list (concat "completion_matches (\"" command "\");\n")))
286 ;; Sort the list
287 (setq inferior-octave-output-list
288 (sort inferior-octave-output-list 'string-lessp))
289 ;; Remove duplicates
290 (let* ((x inferior-octave-output-list)
291 (y (cdr x)))
292 (while y
293 (if (string-equal (car x) (car y))
294 (setcdr x (setq y (cdr y)))
295 (setq x y
296 y (cdr y)))))
297 ;; And let comint handle the rest
298 (comint-dynamic-simple-complete
299 command inferior-octave-output-list)))))
300
301(defun inferior-octave-dynamic-list-input-ring ()
302 "List the buffer's input history in a help buffer"
303 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
304 ;; "completion" by "history reference" ...
305 (interactive)
306 (if (or (not (ring-p comint-input-ring))
307 (ring-empty-p comint-input-ring))
308 (message "No history")
309 (let ((history nil)
310 (history-buffer " *Input History*")
311 (index (1- (ring-length comint-input-ring)))
312 (conf (current-window-configuration)))
313 ;; We have to build up a list ourselves from the ring vector.
314 (while (>= index 0)
315 (setq history (cons (ring-ref comint-input-ring index) history)
316 index (1- index)))
317 ;; Change "completion" to "history reference"
318 ;; to make the display accurate.
319 (with-output-to-temp-buffer history-buffer
320 (display-completion-list history)
321 (set-buffer history-buffer))
322 (message "Hit space to flush")
323 (let ((ch (read-event)))
324 (if (eq ch ?\ )
325 (set-window-configuration conf)
326 (setq unread-command-events (list ch)))))))
327
328(defun inferior-octave-strip-ctrl-g (string)
329 "Strip leading `^G' character.
330If STRING starts with a `^G', ring the bell and strip it."
331 (if (string-match "^\a" string)
332 (progn
333 (ding)
334 (setq string (substring string 1))))
335 string)
336
337(defun inferior-octave-output-filter (proc string)
338 "Standard output filter for the inferior Octave process.
339Ring Emacs bell if process output starts with an ASCII bell, and pass
340the rest to `comint-output-filter'."
341 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
342
343(defun inferior-octave-output-digest (proc string)
344 "Special output filter for the inferior Octave process.
345Save all output between newlines into `inferior-octave-output-list', and
346the rest to `inferior-octave-output-string'."
347 (setq string (concat inferior-octave-output-string string))
348 (while (string-match "\n" string)
349 (setq inferior-octave-output-list
350 (append inferior-octave-output-list
351 (list (substring string 0 (match-beginning 0))))
352 string (substring string (match-end 0))))
353 (if (string-match inferior-octave-prompt string)
354 (setq inferior-octave-receive-in-progress nil))
355 (setq inferior-octave-output-string string))
356
357(defun inferior-octave-send-list-and-digest (list)
358 "Send LIST to the inferior Octave process and digest the output.
359The elements of LIST have to be strings and are sent one by one. All
360output is passed to the filter `inferior-octave-output-digest'."
361 (let* ((proc inferior-octave-process)
362 (filter (process-filter proc))
363 string)
364 (set-process-filter proc 'inferior-octave-output-digest)
365 (setq inferior-octave-output-list nil)
366 (unwind-protect
367 (while (setq string (car list))
368 (setq inferior-octave-output-string nil
369 inferior-octave-receive-in-progress t)
370 (comint-send-string proc string)
371 (while inferior-octave-receive-in-progress
372 (accept-process-output proc))
373 (setq list (cdr list)))
374 (set-process-filter proc filter))))
375
376(defun inferior-octave-directory-tracker (string)
377 "Tracks `cd' commands issued to the inferior Octave process.
378Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
df287ddb
SE
379 (cond
380 ((string-match "^[ \t]*cd[ \t;]*$" string)
381 (cd "~"))
382 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
383 (cd (substring string (match-beginning 1) (match-end 1))))))
081bf30e
RS
384
385(defun inferior-octave-resync-dirs ()
386 "Resync the buffer's idea of the current directory.
387This command queries the inferior Octave process about its current
388directory and makes this the current buffer's default directory."
389 (interactive)
390 (inferior-octave-send-list-and-digest '("pwd\n"))
391 (cd (car inferior-octave-output-list)))
392
15c2d736
KH
393;;; provide ourself
394
395(provide 'octave-inf)
396
ab5796a9 397;;; arch-tag: bdce0395-24d1-4bb4-bfba-6fb1eeb1a660
e96a83c8 398;;; octave-inf.el ends here