(shell-mode): Use define-derived-mode.
[emacs.git] / lisp / progmodes / octave-inf.el
blobdd26918e4e2fc12369a00aae3f05a6c6a09b9687
1 ;;; octave-inf.el --- running Octave as an inferior Emacs process
3 ;; Copyright (C) 1997 Free Software Foundation, Inc.
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
10 ;; This file is part of GNU Emacs.
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.
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.
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.
27 ;;; Code:
29 (require 'octave-mod)
30 (require 'comint)
32 (defgroup octave-inferior nil
33 "Running Octave as an inferior Emacs process."
34 :group 'octave)
36 (defcustom inferior-octave-program "octave"
37 "*Program invoked by `inferior-octave'."
38 :type 'string
39 :group 'octave-inferior)
41 (defcustom inferior-octave-prompt
42 "\\(^octave\\(\\|.bin\\)\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
43 "*Regexp to match prompts for the inferior Octave process."
44 :type 'regexp
45 :group 'octave-inferior)
47 (defcustom inferior-octave-startup-file nil
48 "*Name of the inferior Octave startup file.
49 The contents of this file are sent to the inferior Octave process on
50 startup."
51 :type '(choice (const :tag "None" nil)
52 file)
53 :group 'octave-inferior)
55 (defcustom inferior-octave-startup-args nil
56 "*List of command line arguments for the inferior Octave process.
57 For example, for suppressing the startup message and using `traditional'
58 mode, set this to (\"-q\" \"--traditional\")."
59 :type '(repeat string)
60 :group 'octave-inferior)
62 (defvar inferior-octave-mode-map nil
63 "Keymap used in Inferior Octave mode.")
64 (if inferior-octave-mode-map
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)))
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
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)))
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)
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.")
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.
102 The prompt is assumed to be any text at the beginning of the line matching
103 the regular expression `comint-prompt-regexp', a buffer local variable."
104 (save-excursion (comint-bol nil) (point))))
107 (defvar inferior-octave-output-list nil)
108 (defvar inferior-octave-output-string nil)
109 (defvar inferior-octave-receive-in-progress nil)
111 (defvar inferior-octave-startup-hook nil)
113 (defvar inferior-octave-complete-impossible nil
114 "Non-nil means that `inferior-octave-complete' is impossible.")
116 (defvar inferior-octave-dynamic-complete-functions
117 '(inferior-octave-complete comint-dynamic-complete-filename)
118 "List of functions called to perform completion for inferior Octave.
119 This variable is used to initialize `comint-dynamic-complete-functions'
120 in the Inferior Octave buffer.")
122 (defun inferior-octave-mode ()
123 "Major mode for interacting with an inferior Octave process.
124 Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
125 buffer.
127 Entry to this mode successively runs the hooks `comint-mode-hook' and
128 `inferior-octave-mode-hook'."
129 (interactive)
130 (comint-mode)
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)
139 (make-local-variable 'comment-start)
140 (setq comment-start octave-comment-start)
141 (make-local-variable 'comment-end)
142 (setq comment-end "")
143 (make-local-variable 'comment-column)
144 (setq comment-column 32)
145 (make-local-variable 'comment-start-skip)
146 (setq comment-start-skip octave-comment-start-skip)
148 (make-local-variable 'font-lock-defaults)
149 (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))
151 (setq comint-input-ring-file-name
152 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
153 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024)
154 comint-input-filter-functions '(inferior-octave-directory-tracker)
155 comint-dynamic-complete-functions
156 inferior-octave-dynamic-complete-functions)
157 (comint-read-input-ring t)
159 (run-hooks 'inferior-octave-mode-hook))
161 ;;;###autoload
162 (defun inferior-octave (&optional arg)
163 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
164 This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
166 Unless ARG is non-nil, switches to this buffer.
168 The elements of the list `inferior-octave-startup-args' are sent as
169 command line arguments to the inferior Octave process on startup.
171 Additional commands to be executed on startup can be provided either in
172 the file specified by `inferior-octave-startup-file' or by the default
173 startup file, `~/.emacs-octave'."
174 (interactive "P")
175 (let ((buffer inferior-octave-buffer))
176 (get-buffer-create buffer)
177 (if (comint-check-proc buffer)
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))))
187 ;;;###autoload
188 (defalias 'run-octave 'inferior-octave)
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
196 (append (list "-i" "--no-line-editing")
197 inferior-octave-startup-args))))
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)
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"))))
220 ;; O.k., now we are ready for the Inferior Octave startup commands.
221 (let* (commands
222 (program (file-name-nondirectory inferior-octave-program))
223 (file (or inferior-octave-startup-file
224 (concat "~/.emacs-" program))))
225 (setq commands
226 (list "page_screen_output = 0;\n"
227 (if (not (string-equal
228 inferior-octave-output-string ">> "))
229 "PS1=\"\\\\s> \";\n")
230 (if (file-exists-p file)
231 (format "source (\"%s\");\n" file))))
232 (inferior-octave-send-list-and-digest commands))
233 (insert-before-markers
234 (concat
235 (if inferior-octave-output-list
236 (concat (mapconcat
237 'identity inferior-octave-output-list "\n")
238 "\n"))
239 inferior-octave-output-string))
240 ;; Next, we check whether Octave supports `completion_matches' ...
241 (inferior-octave-send-list-and-digest
242 (list "exist \"completion_matches\"\n"))
243 (setq inferior-octave-complete-impossible
244 (not (string-match "5$" (car inferior-octave-output-list))))
246 ;; And finally, everything is back to normal.
247 (set-process-filter proc 'inferior-octave-output-filter)
248 (run-hooks 'inferior-octave-startup-hook)))
251 (defun inferior-octave-complete ()
252 "Perform completion on the Octave symbol preceding point.
253 This is implemented using the Octave command `completion_matches' which
254 is NOT available with versions of Octave prior to 2.0."
255 (interactive)
256 (let* ((end (point))
257 (command
258 (save-excursion
259 (skip-syntax-backward "w_" (comint-line-beginning-position))
260 (buffer-substring-no-properties (point) end)))
261 (proc (get-buffer-process inferior-octave-buffer))
262 (filter (process-filter proc)))
263 (cond (inferior-octave-complete-impossible
264 (error (concat
265 "Your Octave does not have `completion_matches'. "
266 "Please upgrade to version 2.X.")))
267 ((string-equal command "")
268 (message "Cannot complete an empty string"))
270 (inferior-octave-send-list-and-digest
271 (list (concat "completion_matches (\"" command "\");\n")))
272 ;; Sort the list
273 (setq inferior-octave-output-list
274 (sort inferior-octave-output-list 'string-lessp))
275 ;; Remove duplicates
276 (let* ((x inferior-octave-output-list)
277 (y (cdr x)))
278 (while y
279 (if (string-equal (car x) (car y))
280 (setcdr x (setq y (cdr y)))
281 (setq x y
282 y (cdr y)))))
283 ;; And let comint handle the rest
284 (comint-dynamic-simple-complete
285 command inferior-octave-output-list)))))
287 (defun inferior-octave-dynamic-list-input-ring ()
288 "List the buffer's input history in a help buffer"
289 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
290 ;; "completion" by "history reference" ...
291 (interactive)
292 (if (or (not (ring-p comint-input-ring))
293 (ring-empty-p comint-input-ring))
294 (message "No history")
295 (let ((history nil)
296 (history-buffer " *Input History*")
297 (index (1- (ring-length comint-input-ring)))
298 (conf (current-window-configuration)))
299 ;; We have to build up a list ourselves from the ring vector.
300 (while (>= index 0)
301 (setq history (cons (ring-ref comint-input-ring index) history)
302 index (1- index)))
303 ;; Change "completion" to "history reference"
304 ;; to make the display accurate.
305 (with-output-to-temp-buffer history-buffer
306 (display-completion-list history)
307 (set-buffer history-buffer))
308 (message "Hit space to flush")
309 (let ((ch (read-event)))
310 (if (eq ch ?\ )
311 (set-window-configuration conf)
312 (setq unread-command-events (list ch)))))))
314 (defun inferior-octave-strip-ctrl-g (string)
315 "Strip leading `^G' character.
316 If STRING starts with a `^G', ring the bell and strip it."
317 (if (string-match "^\a" string)
318 (progn
319 (ding)
320 (setq string (substring string 1))))
321 string)
323 (defun inferior-octave-output-filter (proc string)
324 "Standard output filter for the inferior Octave process.
325 Ring Emacs bell if process output starts with an ASCII bell, and pass
326 the rest to `comint-output-filter'."
327 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
329 (defun inferior-octave-output-digest (proc string)
330 "Special output filter for the inferior Octave process.
331 Save all output between newlines into `inferior-octave-output-list', and
332 the rest to `inferior-octave-output-string'."
333 (setq string (concat inferior-octave-output-string string))
334 (while (string-match "\n" string)
335 (setq inferior-octave-output-list
336 (append inferior-octave-output-list
337 (list (substring string 0 (match-beginning 0))))
338 string (substring string (match-end 0))))
339 (if (string-match inferior-octave-prompt string)
340 (setq inferior-octave-receive-in-progress nil))
341 (setq inferior-octave-output-string string))
343 (defun inferior-octave-send-list-and-digest (list)
344 "Send LIST to the inferior Octave process and digest the output.
345 The elements of LIST have to be strings and are sent one by one. All
346 output is passed to the filter `inferior-octave-output-digest'."
347 (let* ((proc inferior-octave-process)
348 (filter (process-filter proc))
349 string)
350 (set-process-filter proc 'inferior-octave-output-digest)
351 (setq inferior-octave-output-list nil)
352 (unwind-protect
353 (while (setq string (car list))
354 (setq inferior-octave-output-string nil
355 inferior-octave-receive-in-progress t)
356 (comint-send-string proc string)
357 (while inferior-octave-receive-in-progress
358 (accept-process-output proc))
359 (setq list (cdr list)))
360 (set-process-filter proc filter))))
362 (defun inferior-octave-directory-tracker (string)
363 "Tracks `cd' commands issued to the inferior Octave process.
364 Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
365 (cond
366 ((string-match "^[ \t]*cd[ \t;]*$" string)
367 (cd "~"))
368 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
369 (cd (substring string (match-beginning 1) (match-end 1))))))
371 (defun inferior-octave-resync-dirs ()
372 "Resync the buffer's idea of the current directory.
373 This command queries the inferior Octave process about its current
374 directory and makes this the current buffer's default directory."
375 (interactive)
376 (inferior-octave-send-list-and-digest '("pwd\n"))
377 (cd (car inferior-octave-output-list)))
379 ;;; provide ourself
381 (provide 'octave-inf)
383 ;;; octave-inf.el ends here