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