octave-inf.el 14.4 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1
;;; octave-inf.el --- running Octave as an inferior Emacs process
Richard M. Stallman's avatar
Richard M. Stallman committed
2

Nick Roberts's avatar
Nick Roberts committed
3
;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006
4
;; Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
5

6
;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
Richard M. Stallman's avatar
Richard M. Stallman committed
7
;; Author: John Eaton <jwe@bevo.che.wisc.edu>
8
;; Maintainer: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
Richard M. Stallman's avatar
Richard M. Stallman committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
;; Keywords: languages

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
25 26
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
27

28 29
;;; Commentary:

Richard M. Stallman's avatar
Richard M. Stallman committed
30 31
;;; Code:

32
(require 'octave-mod)
Richard M. Stallman's avatar
Richard M. Stallman committed
33 34
(require 'comint)

Andreas Schwab's avatar
Andreas Schwab committed
35 36 37
(defgroup octave-inferior nil
  "Running Octave as an inferior Emacs process."
  :group 'octave)
Richard M. Stallman's avatar
Richard M. Stallman committed
38

Andreas Schwab's avatar
Andreas Schwab committed
39
(defcustom inferior-octave-program "octave"
40
  "Program invoked by `inferior-octave'."
Andreas Schwab's avatar
Andreas Schwab committed
41 42 43 44
  :type 'string
  :group 'octave-inferior)

(defcustom inferior-octave-prompt
45
  "\\(^octave\\(\\|.bin\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
46
  "Regexp to match prompts for the inferior Octave process."
Andreas Schwab's avatar
Andreas Schwab committed
47 48
  :type 'regexp
  :group 'octave-inferior)
Richard M. Stallman's avatar
Richard M. Stallman committed
49

Andreas Schwab's avatar
Andreas Schwab committed
50
(defcustom inferior-octave-startup-file nil
51
  "Name of the inferior Octave startup file.
Richard M. Stallman's avatar
Richard M. Stallman committed
52
The contents of this file are sent to the inferior Octave process on
Andreas Schwab's avatar
Andreas Schwab committed
53 54 55 56
startup."
  :type '(choice (const :tag "None" nil)
		 file)
  :group 'octave-inferior)
Richard M. Stallman's avatar
Richard M. Stallman committed
57

58
(defcustom inferior-octave-startup-args nil
59
  "List of command line arguments for the inferior Octave process.
Richard M. Stallman's avatar
Richard M. Stallman committed
60
For example, for suppressing the startup message and using `traditional'
Andreas Schwab's avatar
Andreas Schwab committed
61 62 63
mode, set this to (\"-q\" \"--traditional\")."
  :type '(repeat string)
  :group 'octave-inferior)
Richard M. Stallman's avatar
Richard M. Stallman committed
64

65 66 67
(defvar inferior-octave-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map comint-mode-map)
Richard M. Stallman's avatar
Richard M. Stallman committed
68 69 70 71 72 73
    (define-key map "\t" 'comint-dynamic-complete)
    (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
    (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
    (define-key map [menu-bar inout list-history]
      '("List Input History" . inferior-octave-dynamic-list-input-ring))
    (define-key map "\C-c\C-h" 'octave-help)
74 75
    map)
  "Keymap used in Inferior Octave mode.")
Richard M. Stallman's avatar
Richard M. Stallman committed
76

77
(defvar inferior-octave-mode-syntax-table
Richard M. Stallman's avatar
Richard M. Stallman committed
78 79 80 81
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?\` "w" table)
    (modify-syntax-entry ?\# "<" table)
    (modify-syntax-entry ?\n ">" table)
82 83
    table)
  "Syntax table in use in inferior-octave-mode buffers.")
Richard M. Stallman's avatar
Richard M. Stallman committed
84

Andreas Schwab's avatar
Andreas Schwab committed
85 86 87 88
(defcustom inferior-octave-mode-hook nil
  "*Hook to be run when Inferior Octave mode is started."
  :type 'hook
  :group 'octave-inferior)
Richard M. Stallman's avatar
Richard M. Stallman committed
89 90 91 92 93 94 95

(defvar inferior-octave-font-lock-keywords
  (list
   (cons inferior-octave-prompt 'font-lock-type-face))
  ;; Could certainly do more font locking in inferior Octave ...
  "Additional expressions to highlight in Inferior Octave mode.")

96 97 98 99 100 101 102 103 104 105 106

;;; Compatibility functions
(if (not (fboundp 'comint-line-beginning-position))
    ;; comint-line-beginning-position is defined in Emacs 21
    (defun comint-line-beginning-position ()
      "Returns the buffer position of the beginning of the line, after any prompt.
The prompt is assumed to be any text at the beginning of the line matching
the regular expression `comint-prompt-regexp', a buffer local variable."
      (save-excursion (comint-bol nil) (point))))


Richard M. Stallman's avatar
Richard M. Stallman committed
107 108 109 110 111 112 113 114 115 116
(defvar inferior-octave-output-list nil)
(defvar inferior-octave-output-string nil)
(defvar inferior-octave-receive-in-progress nil)

(defvar inferior-octave-startup-hook nil)

(defvar inferior-octave-complete-impossible nil
  "Non-nil means that `inferior-octave-complete' is impossible.")

(defvar inferior-octave-dynamic-complete-functions
117
  '(inferior-octave-complete comint-dynamic-complete-filename)
Richard M. Stallman's avatar
Richard M. Stallman committed
118 119 120 121 122 123 124 125 126 127 128 129
  "List of functions called to perform completion for inferior Octave.
This variable is used to initialize `comint-dynamic-complete-functions'
in the Inferior Octave buffer.")

(defun inferior-octave-mode ()
  "Major mode for interacting with an inferior Octave process.
Runs Octave as a subprocess of Emacs, with Octave I/O through an Emacs
buffer.

Entry to this mode successively runs the hooks `comint-mode-hook' and
`inferior-octave-mode-hook'."
  (interactive)
130
  (delay-mode-hooks (comint-mode))
Richard M. Stallman's avatar
Richard M. Stallman committed
131 132 133 134 135 136 137 138
  (setq comint-prompt-regexp inferior-octave-prompt
	major-mode 'inferior-octave-mode
	mode-name "Inferior Octave"
	mode-line-process '(":%s")
	local-abbrev-table octave-abbrev-table)
  (use-local-map inferior-octave-mode-map)
  (set-syntax-table inferior-octave-mode-syntax-table)

139
  (make-local-variable 'comment-start)
Richard M. Stallman's avatar
Richard M. Stallman committed
140 141 142 143
  (setq comment-start octave-comment-start)
  (make-local-variable 'comment-end)
  (setq comment-end "")
  (make-local-variable 'comment-column)
144
  (setq comment-column 32)
Richard M. Stallman's avatar
Richard M. Stallman committed
145 146 147 148 149 150 151 152 153
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip octave-comment-start-skip)

  (make-local-variable 'font-lock-defaults)
  (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))

  (setq comint-input-ring-file-name
	(or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
	comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024)
154 155 156
	comint-input-filter-functions '(inferior-octave-directory-tracker))
  (set (make-local-variable 'comint-dynamic-complete-functions)
       inferior-octave-dynamic-complete-functions)
Richard M. Stallman's avatar
Richard M. Stallman committed
157 158
  (comint-read-input-ring t)

159
  (run-mode-hooks 'inferior-octave-mode-hook))
Richard M. Stallman's avatar
Richard M. Stallman committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

;;;###autoload
(defun inferior-octave (&optional arg)
  "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
This buffer is put in Inferior Octave mode.  See `inferior-octave-mode'.

Unless ARG is non-nil, switches to this buffer.

The elements of the list `inferior-octave-startup-args' are sent as
command line arguments to the inferior Octave process on startup.

Additional commands to be executed on startup can be provided either in
the file specified by `inferior-octave-startup-file' or by the default
startup file, `~/.emacs-octave'."
  (interactive "P")
  (let ((buffer inferior-octave-buffer))
    (get-buffer-create buffer)
    (if (comint-check-proc buffer)
	()
      (save-excursion
	(set-buffer buffer)
	(comint-mode)
	(inferior-octave-startup)
	(inferior-octave-mode)))
    (if (not arg)
	(pop-to-buffer buffer))))

;;;###autoload
(defalias 'run-octave 'inferior-octave)

(defun inferior-octave-startup ()
  "Start an inferior Octave process."
  (let ((proc (comint-exec-1
	       (substring inferior-octave-buffer 1 -1)
	       inferior-octave-buffer
	       inferior-octave-program
196 197
	       (append (list "-i" "--no-line-editing")
		       inferior-octave-startup-args))))
Richard M. Stallman's avatar
Richard M. Stallman committed
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    (set-process-filter proc 'inferior-octave-output-digest)
    (setq comint-ptyp process-connection-type
	  inferior-octave-process proc
	  inferior-octave-output-list nil
	  inferior-octave-output-string nil
	  inferior-octave-receive-in-progress t)

    ;; This may look complicated ... However, we need to make sure that
    ;; we additional startup code only AFTER Octave is ready (otherwise,
    ;; output may be mixed up).  Hence, we need to digest the Octave
    ;; output to see when it issues a prompt.
    (while inferior-octave-receive-in-progress
      (accept-process-output inferior-octave-process))
    (goto-char (point-max))
    (set-marker (process-mark proc) (point))
    (insert-before-markers
     (concat
      (if (not (bobp)) "\n")
      (if inferior-octave-output-list
	  (concat (mapconcat
		   'identity inferior-octave-output-list "\n")
		  "\n"))))
220 221 222 223 224 225 226

    ;; An empty secondary prompt, as e.g. obtained by '--braindead',
    ;; means trouble.
    (inferior-octave-send-list-and-digest (list "PS2\n"))
    (if (string-match "^PS2 = *$" (car inferior-octave-output-list))
	(inferior-octave-send-list-and-digest (list "PS2 = \"> \"\n")))

Richard M. Stallman's avatar
Richard M. Stallman committed
227 228 229 230 231 232 233 234
    ;; O.k., now we are ready for the Inferior Octave startup commands.
    (let* (commands
	   (program (file-name-nondirectory inferior-octave-program))
	   (file (or inferior-octave-startup-file
			  (concat "~/.emacs-" program))))
      (setq commands
	    (list "page_screen_output = 0;\n"
		  (if (not (string-equal
235
			    inferior-octave-output-string ">> "))
Richard M. Stallman's avatar
Richard M. Stallman committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
		      "PS1=\"\\\\s> \";\n")
		  (if (file-exists-p file)
		      (format "source (\"%s\");\n" file))))
      (inferior-octave-send-list-and-digest commands))
    (insert-before-markers
     (concat
      (if inferior-octave-output-list
	  (concat (mapconcat
		   'identity inferior-octave-output-list "\n")
		  "\n"))
      inferior-octave-output-string))
    ;; Next, we check whether Octave supports `completion_matches' ...
    (inferior-octave-send-list-and-digest
     (list "exist \"completion_matches\"\n"))
    (setq inferior-octave-complete-impossible
	  (not (string-match "5$" (car inferior-octave-output-list))))

    ;; And finally, everything is back to normal.
    (set-process-filter proc 'inferior-octave-output-filter)
255 256 257 258 259
    (run-hooks 'inferior-octave-startup-hook)
    (run-hooks 'inferior-octave-startup-hook)
    ;; Just in case, to be sure a cd in the startup file
    ;; won't have detrimental effects.
    (inferior-octave-resync-dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
260 261 262 263 264 265 266 267


(defun inferior-octave-complete ()
  "Perform completion on the Octave symbol preceding point.
This is implemented using the Octave command `completion_matches' which
is NOT available with versions of Octave prior to 2.0."
  (interactive)
  (let* ((end (point))
268 269 270 271
	 (command
	  (save-excursion
	    (skip-syntax-backward "w_" (comint-line-beginning-position))
	    (buffer-substring-no-properties (point) end)))
272
	 (proc (get-buffer-process inferior-octave-buffer)))
Richard M. Stallman's avatar
Richard M. Stallman committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
    (cond (inferior-octave-complete-impossible
	   (error (concat
		   "Your Octave does not have `completion_matches'.  "
		   "Please upgrade to version 2.X.")))
	  ((string-equal command "")
	   (message "Cannot complete an empty string"))
	  (t
	   (inferior-octave-send-list-and-digest
	    (list (concat "completion_matches (\"" command "\");\n")))
	   ;; Sort the list
	   (setq inferior-octave-output-list
		 (sort inferior-octave-output-list 'string-lessp))
	   ;; Remove duplicates
	   (let* ((x inferior-octave-output-list)
		  (y (cdr x)))
	     (while y
	       (if (string-equal (car x) (car y))
		   (setcdr x (setq y (cdr y)))
		 (setq x y
		       y (cdr y)))))
	   ;; And let comint handle the rest
	   (comint-dynamic-simple-complete
	    command inferior-octave-output-list)))))

(defun inferior-octave-dynamic-list-input-ring ()
298
  "List the buffer's input history in a help buffer."
Richard M. Stallman's avatar
Richard M. Stallman committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
  ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
  ;; "completion" by "history reference" ...
  (interactive)
  (if (or (not (ring-p comint-input-ring))
          (ring-empty-p comint-input-ring))
      (message "No history")
    (let ((history nil)
          (history-buffer " *Input History*")
          (index (1- (ring-length comint-input-ring)))
          (conf (current-window-configuration)))
      ;; We have to build up a list ourselves from the ring vector.
      (while (>= index 0)
        (setq history (cons (ring-ref comint-input-ring index) history)
              index (1- index)))
      ;; Change "completion" to "history reference"
      ;; to make the display accurate.
      (with-output-to-temp-buffer history-buffer
        (display-completion-list history)
        (set-buffer history-buffer))
      (message "Hit space to flush")
      (let ((ch (read-event)))
        (if (eq ch ?\ )
            (set-window-configuration conf)
          (setq unread-command-events (list ch)))))))

(defun inferior-octave-strip-ctrl-g (string)
  "Strip leading `^G' character.
If STRING starts with a `^G', ring the bell and strip it."
  (if (string-match "^\a" string)
      (progn
        (ding)
        (setq string (substring string 1))))
  string)

(defun inferior-octave-output-filter (proc string)
  "Standard output filter for the inferior Octave process.
Ring Emacs bell if process output starts with an ASCII bell, and pass
the rest to `comint-output-filter'."
  (comint-output-filter proc (inferior-octave-strip-ctrl-g string)))

(defun inferior-octave-output-digest (proc string)
  "Special output filter for the inferior Octave process.
Save all output between newlines into `inferior-octave-output-list', and
the rest to `inferior-octave-output-string'."
  (setq string (concat inferior-octave-output-string string))
  (while (string-match "\n" string)
    (setq inferior-octave-output-list
	  (append inferior-octave-output-list
		  (list (substring string 0 (match-beginning 0))))
	  string (substring string (match-end 0))))
  (if (string-match inferior-octave-prompt string)
      (setq inferior-octave-receive-in-progress nil))
  (setq inferior-octave-output-string string))

(defun inferior-octave-send-list-and-digest (list)
  "Send LIST to the inferior Octave process and digest the output.
The elements of LIST have to be strings and are sent one by one.  All
output is passed to the filter `inferior-octave-output-digest'."
  (let* ((proc inferior-octave-process)
	 (filter (process-filter proc))
	 string)
    (set-process-filter proc 'inferior-octave-output-digest)
    (setq inferior-octave-output-list nil)
    (unwind-protect
	(while (setq string (car list))
	  (setq inferior-octave-output-string nil
		inferior-octave-receive-in-progress t)
	  (comint-send-string proc string)
	  (while inferior-octave-receive-in-progress
	    (accept-process-output proc))
	  (setq list (cdr list)))
      (set-process-filter proc filter))))

(defun inferior-octave-directory-tracker (string)
  "Tracks `cd' commands issued to the inferior Octave process.
Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
375 376 377 378 379
  (cond
   ((string-match "^[ \t]*cd[ \t;]*$" string)
    (cd "~"))
   ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
    (cd (substring string (match-beginning 1) (match-end 1))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
380 381 382 383 384 385 386 387 388

(defun inferior-octave-resync-dirs ()
  "Resync the buffer's idea of the current directory.
This command queries the inferior Octave process about its current
directory and makes this the current buffer's default directory."
  (interactive)
  (inferior-octave-send-list-and-digest '("pwd\n"))
  (cd (car inferior-octave-output-list)))

Karl Heuer's avatar
Karl Heuer committed
389 390 391 392
;;; provide ourself

(provide 'octave-inf)

393
;; arch-tag: bdce0395-24d1-4bb4-bfba-6fb1eeb1a660
Richard M. Stallman's avatar
Richard M. Stallman committed
394
;;; octave-inf.el ends here