* textmodes/reftex-vars.el
[bpt/emacs.git] / lisp / progmodes / octave-inf.el
CommitLineData
092af6d8 1;;; octave-inf.el --- running Octave as an inferior Emacs process
081bf30e 2
ab422c4d 3;; Copyright (C) 1997, 2001-2013 Free Software Foundation, Inc.
081bf30e 4
d0943d40 5;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
daf3dc79 6;; John Eaton <jwe@bevo.che.wisc.edu>
71d4c2a5 7;; Maintainer: FSF
081bf30e 8;; Keywords: languages
aad4679e 9;; Package: octave-mod
081bf30e
RS
10
11;; This file is part of GNU Emacs.
12
b1fc2b50 13;; GNU Emacs is free software: you can redistribute it and/or modify
081bf30e 14;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
081bf30e
RS
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
b1fc2b50 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
081bf30e 25
e8af40ee
PJ
26;;; Commentary:
27
081bf30e
RS
28;;; Code:
29
f7bbab75 30(require 'octave-mod)
081bf30e
RS
31(require 'comint)
32
28d16ed3
AS
33(defgroup octave-inferior nil
34 "Running Octave as an inferior Emacs process."
35 :group 'octave)
081bf30e 36
28d16ed3 37(defcustom inferior-octave-program "octave"
e2536e57 38 "Program invoked by `inferior-octave'."
28d16ed3
AS
39 :type 'string
40 :group 'octave-inferior)
41
42(defcustom inferior-octave-prompt
6145f7fd 43 "\\(^octave\\(\\|.bin\\|.exe\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
e2536e57 44 "Regexp to match prompts for the inferior Octave process."
28d16ed3
AS
45 :type 'regexp
46 :group 'octave-inferior)
081bf30e 47
28d16ed3 48(defcustom inferior-octave-startup-file nil
e2536e57 49 "Name of the inferior Octave startup file.
081bf30e 50The contents of this file are sent to the inferior Octave process on
28d16ed3
AS
51startup."
52 :type '(choice (const :tag "None" nil)
53 file)
54 :group 'octave-inferior)
081bf30e 55
3a5a5d36 56(defcustom inferior-octave-startup-args nil
e2536e57 57 "List of command line arguments for the inferior Octave process.
081bf30e 58For example, for suppressing the startup message and using `traditional'
28d16ed3
AS
59mode, set this to (\"-q\" \"--traditional\")."
60 :type '(repeat string)
61 :group 'octave-inferior)
081bf30e 62
e2536e57
SM
63(defvar inferior-octave-mode-map
64 (let ((map (make-sparse-keymap)))
65 (set-keymap-parent map comint-mode-map)
081bf30e
RS
66 (define-key map "\t" 'comint-dynamic-complete)
67 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
68 (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
69 (define-key map [menu-bar inout list-history]
70 '("List Input History" . inferior-octave-dynamic-list-input-ring))
71d4c2a5 71 ;; FIXME: free C-h so it can do the describe-prefix-bindings.
a3839de2 72 (define-key map "\C-c\C-h" 'info-lookup-symbol)
e2536e57
SM
73 map)
74 "Keymap used in Inferior Octave mode.")
081bf30e 75
e2536e57 76(defvar inferior-octave-mode-syntax-table
e92f3bd3 77 (let ((table (make-syntax-table octave-mode-syntax-table)))
e2536e57
SM
78 table)
79 "Syntax table in use in inferior-octave-mode buffers.")
081bf30e 80
28d16ed3 81(defcustom inferior-octave-mode-hook nil
fb7ada5f 82 "Hook to be run when Inferior Octave mode is started."
28d16ed3
AS
83 :type 'hook
84 :group 'octave-inferior)
081bf30e
RS
85
86(defvar inferior-octave-font-lock-keywords
87 (list
88 (cons inferior-octave-prompt 'font-lock-type-face))
89 ;; Could certainly do more font locking in inferior Octave ...
90 "Additional expressions to highlight in Inferior Octave mode.")
91
9d245da5
MB
92
93;;; Compatibility functions
94(if (not (fboundp 'comint-line-beginning-position))
95 ;; comint-line-beginning-position is defined in Emacs 21
96 (defun comint-line-beginning-position ()
97 "Returns the buffer position of the beginning of the line, after any prompt.
98The prompt is assumed to be any text at the beginning of the line matching
99the regular expression `comint-prompt-regexp', a buffer local variable."
100 (save-excursion (comint-bol nil) (point))))
101
102
081bf30e
RS
103(defvar inferior-octave-output-list nil)
104(defvar inferior-octave-output-string nil)
105(defvar inferior-octave-receive-in-progress nil)
106
107(defvar inferior-octave-startup-hook nil)
108
109(defvar inferior-octave-complete-impossible nil
110 "Non-nil means that `inferior-octave-complete' is impossible.")
111
d32f600d
CY
112(defvar inferior-octave-has-built-in-variables nil
113 "Non-nil means that Octave has built-in variables.")
114
081bf30e 115(defvar inferior-octave-dynamic-complete-functions
e92f3bd3 116 '(inferior-octave-completion-at-point comint-filename-completion)
081bf30e
RS
117 "List of functions called to perform completion for inferior Octave.
118This variable is used to initialize `comint-dynamic-complete-functions'
119in the Inferior Octave buffer.")
120
e92f3bd3
SM
121(defvar info-lookup-mode)
122
175069ef 123(define-derived-mode inferior-octave-mode comint-mode "Inferior Octave"
081bf30e
RS
124 "Major mode for interacting with an inferior Octave process.
125Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
126buffer.
127
128Entry to this mode successively runs the hooks `comint-mode-hook' and
129`inferior-octave-mode-hook'."
081bf30e 130 (setq comint-prompt-regexp inferior-octave-prompt
081bf30e
RS
131 mode-line-process '(":%s")
132 local-abbrev-table octave-abbrev-table)
081bf30e 133
175069ef
SM
134 (set (make-local-variable 'comment-start) octave-comment-start)
135 (set (make-local-variable 'comment-end) "")
136 (set (make-local-variable 'comment-column) 32)
137 (set (make-local-variable 'comment-start-skip) octave-comment-start-skip)
081bf30e 138
175069ef
SM
139 (set (make-local-variable 'font-lock-defaults)
140 '(inferior-octave-font-lock-keywords nil nil))
081bf30e 141
e92f3bd3
SM
142 (set (make-local-variable 'info-lookup-mode) 'octave-mode)
143
081bf30e
RS
144 (setq comint-input-ring-file-name
145 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
e9886e88 146 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024))
e2536e57
SM
147 (set (make-local-variable 'comint-dynamic-complete-functions)
148 inferior-octave-dynamic-complete-functions)
e9886e88
FP
149 (add-hook 'comint-input-filter-functions
150 'inferior-octave-directory-tracker nil t)
175069ef 151 (comint-read-input-ring t))
081bf30e
RS
152
153;;;###autoload
154(defun inferior-octave (&optional arg)
155 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
156This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
157
158Unless ARG is non-nil, switches to this buffer.
159
160The elements of the list `inferior-octave-startup-args' are sent as
161command line arguments to the inferior Octave process on startup.
162
163Additional commands to be executed on startup can be provided either in
164the file specified by `inferior-octave-startup-file' or by the default
165startup file, `~/.emacs-octave'."
166 (interactive "P")
167 (let ((buffer inferior-octave-buffer))
168 (get-buffer-create buffer)
169 (if (comint-check-proc buffer)
170 ()
9a529312 171 (with-current-buffer buffer
081bf30e
RS
172 (comint-mode)
173 (inferior-octave-startup)
174 (inferior-octave-mode)))
175 (if (not arg)
176 (pop-to-buffer buffer))))
177
178;;;###autoload
179(defalias 'run-octave 'inferior-octave)
180
181(defun inferior-octave-startup ()
182 "Start an inferior Octave process."
183 (let ((proc (comint-exec-1
184 (substring inferior-octave-buffer 1 -1)
185 inferior-octave-buffer
186 inferior-octave-program
3a5a5d36
SE
187 (append (list "-i" "--no-line-editing")
188 inferior-octave-startup-args))))
081bf30e
RS
189 (set-process-filter proc 'inferior-octave-output-digest)
190 (setq comint-ptyp process-connection-type
191 inferior-octave-process proc
192 inferior-octave-output-list nil
193 inferior-octave-output-string nil
194 inferior-octave-receive-in-progress t)
195
196 ;; This may look complicated ... However, we need to make sure that
197 ;; we additional startup code only AFTER Octave is ready (otherwise,
198 ;; output may be mixed up). Hence, we need to digest the Octave
199 ;; output to see when it issues a prompt.
200 (while inferior-octave-receive-in-progress
201 (accept-process-output inferior-octave-process))
202 (goto-char (point-max))
203 (set-marker (process-mark proc) (point))
204 (insert-before-markers
205 (concat
206 (if (not (bobp)) "\f\n")
207 (if inferior-octave-output-list
208 (concat (mapconcat
209 'identity inferior-octave-output-list "\n")
210 "\n"))))
107dd21b 211
d32f600d
CY
212 ;; Find out whether Octave has built-in variables.
213 (inferior-octave-send-list-and-digest
214 (list "exist \"LOADPATH\"\n"))
215 (setq inferior-octave-has-built-in-variables
216 (string-match "101$" (car inferior-octave-output-list)))
217
107dd21b
EZ
218 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
219 ;; means trouble.
220 (inferior-octave-send-list-and-digest (list "PS2\n"))
d32f600d
CY
221 (if (string-match "\\(PS2\\|ans\\) = *$" (car inferior-octave-output-list))
222 (inferior-octave-send-list-and-digest
223 (list (if inferior-octave-has-built-in-variables
224 "PS2 = \"> \"\n"
225 "PS2 (\"> \");\n"))))
107dd21b 226
081bf30e
RS
227 ;; O.k., now we are ready for the Inferior Octave startup commands.
228 (let* (commands
229 (program (file-name-nondirectory inferior-octave-program))
230 (file (or inferior-octave-startup-file
231 (concat "~/.emacs-" program))))
232 (setq commands
d32f600d 233 (list "more off;\n"
081bf30e 234 (if (not (string-equal
a1506d29 235 inferior-octave-output-string ">> "))
d32f600d
CY
236 (if inferior-octave-has-built-in-variables
237 "PS1=\"\\\\s> \";\n"
238 "PS1 (\"\\\\s> \");\n"))
081bf30e
RS
239 (if (file-exists-p file)
240 (format "source (\"%s\");\n" file))))
241 (inferior-octave-send-list-and-digest commands))
242 (insert-before-markers
243 (concat
244 (if inferior-octave-output-list
245 (concat (mapconcat
246 'identity inferior-octave-output-list "\n")
247 "\n"))
248 inferior-octave-output-string))
249 ;; Next, we check whether Octave supports `completion_matches' ...
250 (inferior-octave-send-list-and-digest
251 (list "exist \"completion_matches\"\n"))
252 (setq inferior-octave-complete-impossible
253 (not (string-match "5$" (car inferior-octave-output-list))))
254
255 ;; And finally, everything is back to normal.
256 (set-process-filter proc 'inferior-octave-output-filter)
4ca7c468
EZ
257 (run-hooks 'inferior-octave-startup-hook)
258 (run-hooks 'inferior-octave-startup-hook)
259 ;; Just in case, to be sure a cd in the startup file
260 ;; won't have detrimental effects.
261 (inferior-octave-resync-dirs)))
081bf30e
RS
262
263\f
e92f3bd3
SM
264(defun inferior-octave-completion-at-point ()
265 "Return the data to complete the Octave symbol at point."
266 (let* ((end (point))
267 (start
268 (save-excursion
269 (skip-syntax-backward "w_" (comint-line-beginning-position))
270 (point))))
bcd70d97
SM
271 (cond ((eq start end) nil)
272 (inferior-octave-complete-impossible
273 (message (concat
274 "Your Octave does not have `completion_matches'. "
275 "Please upgrade to version 2.X."))
276 nil)
e92f3bd3
SM
277 (t
278 (list
279 start end
280 (completion-table-dynamic
281 (lambda (command)
282 (inferior-octave-send-list-and-digest
283 (list (concat "completion_matches (\"" command "\");\n")))
284 (sort (delete-dups inferior-octave-output-list)
285 'string-lessp))))))))
286
bcd70d97
SM
287(define-obsolete-function-alias 'inferior-octave-complete
288 'completion-at-point "24.1")
081bf30e
RS
289
290(defun inferior-octave-dynamic-list-input-ring ()
e2536e57 291 "List the buffer's input history in a help buffer."
081bf30e
RS
292 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
293 ;; "completion" by "history reference" ...
294 (interactive)
295 (if (or (not (ring-p comint-input-ring))
296 (ring-empty-p comint-input-ring))
297 (message "No history")
298 (let ((history nil)
299 (history-buffer " *Input History*")
300 (index (1- (ring-length comint-input-ring)))
301 (conf (current-window-configuration)))
302 ;; We have to build up a list ourselves from the ring vector.
303 (while (>= index 0)
304 (setq history (cons (ring-ref comint-input-ring index) history)
305 index (1- index)))
306 ;; Change "completion" to "history reference"
307 ;; to make the display accurate.
308 (with-output-to-temp-buffer history-buffer
309 (display-completion-list history)
310 (set-buffer history-buffer))
311 (message "Hit space to flush")
312 (let ((ch (read-event)))
313 (if (eq ch ?\ )
314 (set-window-configuration conf)
315 (setq unread-command-events (list ch)))))))
316
317(defun inferior-octave-strip-ctrl-g (string)
318 "Strip leading `^G' character.
319If STRING starts with a `^G', ring the bell and strip it."
320 (if (string-match "^\a" string)
321 (progn
322 (ding)
323 (setq string (substring string 1))))
324 string)
325
326(defun inferior-octave-output-filter (proc string)
327 "Standard output filter for the inferior Octave process.
328Ring Emacs bell if process output starts with an ASCII bell, and pass
329the rest to `comint-output-filter'."
330 (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))
331
e02f48d7 332(defun inferior-octave-output-digest (_proc string)
081bf30e
RS
333 "Special output filter for the inferior Octave process.
334Save all output between newlines into `inferior-octave-output-list', and
335the rest to `inferior-octave-output-string'."
336 (setq string (concat inferior-octave-output-string string))
337 (while (string-match "\n" string)
338 (setq inferior-octave-output-list
339 (append inferior-octave-output-list
340 (list (substring string 0 (match-beginning 0))))
341 string (substring string (match-end 0))))
342 (if (string-match inferior-octave-prompt string)
343 (setq inferior-octave-receive-in-progress nil))
344 (setq inferior-octave-output-string string))
345
346(defun inferior-octave-send-list-and-digest (list)
347 "Send LIST to the inferior Octave process and digest the output.
348The elements of LIST have to be strings and are sent one by one. All
349output is passed to the filter `inferior-octave-output-digest'."
350 (let* ((proc inferior-octave-process)
081bf30e 351 string)
bcd7a0a4
SM
352 (add-function :override (process-filter proc)
353 #'inferior-octave-output-digest)
081bf30e
RS
354 (setq inferior-octave-output-list nil)
355 (unwind-protect
356 (while (setq string (car list))
357 (setq inferior-octave-output-string nil
358 inferior-octave-receive-in-progress t)
359 (comint-send-string proc string)
360 (while inferior-octave-receive-in-progress
361 (accept-process-output proc))
362 (setq list (cdr list)))
bcd7a0a4
SM
363 (remove-function (process-filter proc)
364 #'inferior-octave-output-digest))))
081bf30e
RS
365
366(defun inferior-octave-directory-tracker (string)
367 "Tracks `cd' commands issued to the inferior Octave process.
368Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
df287ddb
SE
369 (cond
370 ((string-match "^[ \t]*cd[ \t;]*$" string)
371 (cd "~"))
372 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
373 (cd (substring string (match-beginning 1) (match-end 1))))))
081bf30e
RS
374
375(defun inferior-octave-resync-dirs ()
376 "Resync the buffer's idea of the current directory.
377This command queries the inferior Octave process about its current
378directory and makes this the current buffer's default directory."
379 (interactive)
d32f600d 380 (inferior-octave-send-list-and-digest '("disp (pwd ())\n"))
081bf30e
RS
381 (cd (car inferior-octave-output-list)))
382
15c2d736
KH
383;;; provide ourself
384
385(provide 'octave-inf)
386
e96a83c8 387;;; octave-inf.el ends here