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