1 ;;; octave-inf.el --- running Octave as an inferior Emacs process
3 ;; Copyright (C) 1997, 2001-2012 Free Software Foundation, Inc.
5 ;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
6 ;; John Eaton <jwe@bevo.che.wisc.edu>
11 ;; This file is part of GNU Emacs.
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 3 of the License, or
16 ;; (at your option) any later version.
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.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
33 (defgroup octave-inferior nil
34 "Running Octave as an inferior Emacs process."
37 (defcustom inferior-octave-program
"octave"
38 "Program invoked by `inferior-octave'."
40 :group
'octave-inferior
)
42 (defcustom inferior-octave-prompt
43 "\\(^octave\\(\\|.bin\\|.exe\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
44 "Regexp to match prompts for the inferior Octave process."
46 :group
'octave-inferior
)
48 (defcustom inferior-octave-startup-file nil
49 "Name of the inferior Octave startup file.
50 The contents of this file are sent to the inferior Octave process on
52 :type
'(choice (const :tag
"None" nil
)
54 :group
'octave-inferior
)
56 (defcustom inferior-octave-startup-args nil
57 "List of command line arguments for the inferior Octave process.
58 For example, for suppressing the startup message and using `traditional'
59 mode, set this to (\"-q\" \"--traditional\")."
60 :type
'(repeat string
)
61 :group
'octave-inferior
)
63 (defvar inferior-octave-mode-map
64 (let ((map (make-sparse-keymap)))
65 (set-keymap-parent map comint-mode-map
)
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 ;; FIXME: free C-h so it can do the describe-prefix-bindings.
72 (define-key map
"\C-c\C-h" 'info-lookup-symbol
)
74 "Keymap used in Inferior Octave mode.")
76 (defvar inferior-octave-mode-syntax-table
77 (let ((table (make-syntax-table octave-mode-syntax-table
)))
79 "Syntax table in use in inferior-octave-mode buffers.")
81 (defcustom inferior-octave-mode-hook nil
82 "Hook to be run when Inferior Octave mode is started."
84 :group
'octave-inferior
)
86 (defvar inferior-octave-font-lock-keywords
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.")
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.
98 The prompt is assumed to be any text at the beginning of the line matching
99 the regular expression `comint-prompt-regexp', a buffer local variable."
100 (save-excursion (comint-bol nil
) (point))))
103 (defvar inferior-octave-output-list nil
)
104 (defvar inferior-octave-output-string nil
)
105 (defvar inferior-octave-receive-in-progress nil
)
107 (defvar inferior-octave-startup-hook nil
)
109 (defvar inferior-octave-complete-impossible nil
110 "Non-nil means that `inferior-octave-complete' is impossible.")
112 (defvar inferior-octave-has-built-in-variables nil
113 "Non-nil means that Octave has built-in variables.")
115 (defvar inferior-octave-dynamic-complete-functions
116 '(inferior-octave-completion-at-point comint-filename-completion
)
117 "List of functions called to perform completion for inferior Octave.
118 This variable is used to initialize `comint-dynamic-complete-functions'
119 in the Inferior Octave buffer.")
121 (defvar info-lookup-mode
)
123 (define-derived-mode inferior-octave-mode comint-mode
"Inferior Octave"
124 "Major mode for interacting with an inferior Octave process.
125 Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
128 Entry to this mode successively runs the hooks `comint-mode-hook' and
129 `inferior-octave-mode-hook'."
130 (setq comint-prompt-regexp inferior-octave-prompt
131 mode-line-process
'(":%s")
132 local-abbrev-table octave-abbrev-table
)
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
)
139 (set (make-local-variable 'font-lock-defaults
)
140 '(inferior-octave-font-lock-keywords nil nil
))
142 (set (make-local-variable 'info-lookup-mode
) 'octave-mode
)
144 (setq comint-input-ring-file-name
145 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
146 comint-input-ring-size
(or (getenv "OCTAVE_HISTSIZE") 1024))
147 (set (make-local-variable 'comint-dynamic-complete-functions
)
148 inferior-octave-dynamic-complete-functions
)
149 (add-hook 'comint-input-filter-functions
150 'inferior-octave-directory-tracker nil t
)
151 (comint-read-input-ring t
))
154 (defun inferior-octave (&optional arg
)
155 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
156 This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
158 Unless ARG is non-nil, switches to this buffer.
160 The elements of the list `inferior-octave-startup-args' are sent as
161 command line arguments to the inferior Octave process on startup.
163 Additional commands to be executed on startup can be provided either in
164 the file specified by `inferior-octave-startup-file' or by the default
165 startup file, `~/.emacs-octave'."
167 (let ((buffer inferior-octave-buffer
))
168 (get-buffer-create buffer
)
169 (if (comint-check-proc buffer
)
171 (with-current-buffer buffer
173 (inferior-octave-startup)
174 (inferior-octave-mode)))
176 (pop-to-buffer buffer
))))
179 (defalias 'run-octave
'inferior-octave
)
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
187 (append (list "-i" "--no-line-editing")
188 inferior-octave-startup-args
))))
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
)
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
206 (if (not (bobp)) "\f\n")
207 (if inferior-octave-output-list
209 'identity inferior-octave-output-list
"\n")
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
)))
218 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
220 (inferior-octave-send-list-and-digest (list "PS2\n"))
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
225 "PS2 (\"> \");\n"))))
227 ;; O.k., now we are ready for the Inferior Octave startup commands.
229 (program (file-name-nondirectory inferior-octave-program
))
230 (file (or inferior-octave-startup-file
231 (concat "~/.emacs-" program
))))
234 (if (not (string-equal
235 inferior-octave-output-string
">> "))
236 (if inferior-octave-has-built-in-variables
238 "PS1 (\"\\\\s> \");\n"))
239 (if (file-exists-p file
)
240 (format "source (\"%s\");\n" file
))))
241 (inferior-octave-send-list-and-digest commands
))
242 (insert-before-markers
244 (if inferior-octave-output-list
246 'identity inferior-octave-output-list
"\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
))))
255 ;; And finally, everything is back to normal.
256 (set-process-filter proc
'inferior-octave-output-filter
)
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)))
264 (defun inferior-octave-completion-at-point ()
265 "Return the data to complete the Octave symbol at point."
269 (skip-syntax-backward "w_" (comint-line-beginning-position))
271 (cond ((eq start end
) nil
)
272 (inferior-octave-complete-impossible
274 "Your Octave does not have `completion_matches'. "
275 "Please upgrade to version 2.X."))
280 (completion-table-dynamic
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
))))))))
287 (define-obsolete-function-alias 'inferior-octave-complete
288 'completion-at-point
"24.1")
290 (defun inferior-octave-dynamic-list-input-ring ()
291 "List the buffer's input history in a help buffer."
292 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
293 ;; "completion" by "history reference" ...
295 (if (or (not (ring-p comint-input-ring
))
296 (ring-empty-p comint-input-ring
))
297 (message "No history")
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.
304 (setq history
(cons (ring-ref comint-input-ring index
) history
)
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)))
314 (set-window-configuration conf
)
315 (setq unread-command-events
(list ch
)))))))
317 (defun inferior-octave-strip-ctrl-g (string)
318 "Strip leading `^G' character.
319 If STRING starts with a `^G', ring the bell and strip it."
320 (if (string-match "^\a" string
)
323 (setq string
(substring string
1))))
326 (defun inferior-octave-output-filter (proc string
)
327 "Standard output filter for the inferior Octave process.
328 Ring Emacs bell if process output starts with an ASCII bell, and pass
329 the rest to `comint-output-filter'."
330 (comint-output-filter proc
(inferior-octave-strip-ctrl-g string
)))
332 (defun inferior-octave-output-digest (_proc string
)
333 "Special output filter for the inferior Octave process.
334 Save all output between newlines into `inferior-octave-output-list', and
335 the 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
))
346 (defun inferior-octave-send-list-and-digest (list)
347 "Send LIST to the inferior Octave process and digest the output.
348 The elements of LIST have to be strings and are sent one by one. All
349 output is passed to the filter `inferior-octave-output-digest'."
350 (let* ((proc inferior-octave-process
)
351 (filter (process-filter proc
))
353 (set-process-filter proc
'inferior-octave-output-digest
)
354 (setq inferior-octave-output-list nil
)
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
)))
363 (set-process-filter proc filter
))))
365 (defun inferior-octave-directory-tracker (string)
366 "Tracks `cd' commands issued to the inferior Octave process.
367 Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
369 ((string-match "^[ \t]*cd[ \t;]*$" string
)
371 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string
)
372 (cd (substring string
(match-beginning 1) (match-end 1))))))
374 (defun inferior-octave-resync-dirs ()
375 "Resync the buffer's idea of the current directory.
376 This command queries the inferior Octave process about its current
377 directory and makes this the current buffer's default directory."
379 (inferior-octave-send-list-and-digest '("disp (pwd ())\n"))
380 (cd (car inferior-octave-output-list
)))
384 (provide 'octave-inf
)
386 ;;; octave-inf.el ends here