octave-inf.el 14 KB

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