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