* term/xterm.el (xterm--query): Stop after first matching handler. (Bug#14615)
[bpt/emacs.git] / lisp / progmodes / octave.el
1 ;;; octave.el --- editing octave source files under emacs -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 1997, 2001-2013 Free Software Foundation, Inc.
4
5 ;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
6 ;; John Eaton <jwe@octave.org>
7 ;; Maintainer: FSF
8 ;; Keywords: languages
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; This package provides emacs support for Octave. It defines a major
28 ;; mode for editing Octave code and contains code for interacting with
29 ;; an inferior Octave process using comint.
30
31 ;; See the documentation of `octave-mode' and `run-octave' for further
32 ;; information on usage and customization.
33
34 ;;; Code:
35 (require 'comint)
36
37 ;;; For emacs < 24.3.
38 (require 'newcomment)
39 (eval-and-compile
40 (unless (fboundp 'user-error)
41 (defalias 'user-error 'error))
42 (unless (fboundp 'delete-consecutive-dups)
43 (defalias 'delete-consecutive-dups 'delete-dups)))
44 (eval-when-compile
45 (unless (fboundp 'setq-local)
46 (defmacro setq-local (var val)
47 "Set variable VAR to value VAL in current buffer."
48 (list 'set (list 'make-local-variable (list 'quote var)) val))))
49
50 (defgroup octave nil
51 "Editing Octave code."
52 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
53 :group 'languages)
54
55 (define-obsolete-function-alias 'octave-submit-bug-report
56 'report-emacs-bug "24.4")
57
58 (define-abbrev-table 'octave-abbrev-table nil
59 "Abbrev table for Octave's reserved words.
60 Used in `octave-mode' and `inferior-octave-mode' buffers.")
61
62 (defvar octave-comment-char ?#
63 "Character to start an Octave comment.")
64
65 (defvar octave-comment-start (char-to-string octave-comment-char)
66 "Octave-specific `comment-start' (which see).")
67
68 (defvar octave-comment-start-skip "\\(^\\|\\S<\\)\\(?:%!\\|\\s<+\\)\\s-*"
69 "Octave-specific `comment-start-skip' (which see).")
70
71 (defvar octave-begin-keywords
72 '("classdef" "do" "enumeration" "events" "for" "function" "if" "methods"
73 "parfor" "properties" "switch" "try" "unwind_protect" "while"))
74
75 (defvar octave-else-keywords
76 '("case" "catch" "else" "elseif" "otherwise" "unwind_protect_cleanup"))
77
78 (defvar octave-end-keywords
79 '("endclassdef" "endenumeration" "endevents" "endfor" "endfunction" "endif"
80 "endmethods" "endparfor" "endproperties" "endswitch" "end_try_catch"
81 "end_unwind_protect" "endwhile" "until" "end"))
82
83 (defvar octave-reserved-words
84 (append octave-begin-keywords
85 octave-else-keywords
86 octave-end-keywords
87 '("break" "continue" "global" "persistent" "return"))
88 "Reserved words in Octave.")
89
90 (defvar octave-function-header-regexp
91 (concat "^\\s-*\\_<\\(function\\)\\_>"
92 "\\([^=;(\n]*=[ \t]*\\|[ \t]*\\)\\(\\(?:\\w\\|\\s_\\)+\\)\\_>")
93 "Regexp to match an Octave function header.
94 The string `function' and its name are given by the first and third
95 parenthetical grouping.")
96
97 \f
98 (defvar octave-mode-map
99 (let ((map (make-sparse-keymap)))
100 (define-key map "\M-." 'octave-find-definition)
101 (define-key map "\M-\C-j" 'octave-indent-new-comment-line)
102 (define-key map "\C-c\C-p" 'octave-previous-code-line)
103 (define-key map "\C-c\C-n" 'octave-next-code-line)
104 (define-key map "\C-c\C-a" 'octave-beginning-of-line)
105 (define-key map "\C-c\C-e" 'octave-end-of-line)
106 (define-key map [remap down-list] 'smie-down-list)
107 (define-key map "\C-c\M-\C-h" 'octave-mark-block)
108 (define-key map "\C-c]" 'smie-close-block)
109 (define-key map "\C-c/" 'smie-close-block)
110 (define-key map "\C-c;" 'octave-update-function-file-comment)
111 (define-key map "\C-hd" 'octave-help)
112 (define-key map "\C-c\C-f" 'octave-insert-defun)
113 (define-key map "\C-c\C-il" 'octave-send-line)
114 (define-key map "\C-c\C-ib" 'octave-send-block)
115 (define-key map "\C-c\C-if" 'octave-send-defun)
116 (define-key map "\C-c\C-ir" 'octave-send-region)
117 (define-key map "\C-c\C-is" 'octave-show-process-buffer)
118 (define-key map "\C-c\C-iq" 'octave-hide-process-buffer)
119 (define-key map "\C-c\C-ik" 'octave-kill-process)
120 (define-key map "\C-c\C-i\C-l" 'octave-send-line)
121 (define-key map "\C-c\C-i\C-b" 'octave-send-block)
122 (define-key map "\C-c\C-i\C-f" 'octave-send-defun)
123 (define-key map "\C-c\C-i\C-r" 'octave-send-region)
124 (define-key map "\C-c\C-i\C-s" 'octave-show-process-buffer)
125 (define-key map "\C-c\C-i\C-q" 'octave-hide-process-buffer)
126 (define-key map "\C-c\C-i\C-k" 'octave-kill-process)
127 map)
128 "Keymap used in Octave mode.")
129
130
131
132 (easy-menu-define octave-mode-menu octave-mode-map
133 "Menu for Octave mode."
134 '("Octave"
135 ["Split Line at Point" octave-indent-new-comment-line t]
136 ["Previous Code Line" octave-previous-code-line t]
137 ["Next Code Line" octave-next-code-line t]
138 ["Begin of Line" octave-beginning-of-line t]
139 ["End of Line" octave-end-of-line t]
140 ["Mark Block" octave-mark-block t]
141 ["Close Block" smie-close-block t]
142 "---"
143 ["Start Octave Process" run-octave t]
144 ["Documentation Lookup" info-lookup-symbol t]
145 ["Help on Function" octave-help t]
146 ["Find Function Definition" octave-find-definition t]
147 ["Insert Function" octave-insert-defun t]
148 ["Update Function File Comment" octave-update-function-file-comment t]
149 "---"
150 ["Function Syntax Hints" (call-interactively
151 (if (fboundp 'eldoc-post-insert-mode)
152 'eldoc-post-insert-mode
153 'eldoc-mode))
154 :style toggle :selected (or eldoc-post-insert-mode eldoc-mode)
155 :help "Display function signatures after typing `SPC' or `('"]
156 ["Delimiter Matching" show-paren-mode
157 :style toggle :selected show-paren-mode
158 :help "Highlight matched pairs such as `if ... end'"
159 :visible (fboundp 'smie--matching-block-data)]
160 ["Auto Fill" auto-fill-mode
161 :style toggle :selected auto-fill-function
162 :help "Automatic line breaking"]
163 ["Electric Layout" electric-layout-mode
164 :style toggle :selected electric-layout-mode
165 :help "Automatically insert newlines around some chars"]
166 "---"
167 ("Debug"
168 ["Send Current Line" octave-send-line t]
169 ["Send Current Block" octave-send-block t]
170 ["Send Current Function" octave-send-defun t]
171 ["Send Region" octave-send-region t]
172 ["Show Process Buffer" octave-show-process-buffer t]
173 ["Hide Process Buffer" octave-hide-process-buffer t]
174 ["Kill Process" octave-kill-process t])
175 "---"
176 ["Customize Octave" (customize-group 'octave) t]
177 ["Submit Bug Report" report-emacs-bug t]))
178
179 (defvar octave-mode-syntax-table
180 (let ((table (make-syntax-table)))
181 (modify-syntax-entry ?\r " " table)
182 (modify-syntax-entry ?+ "." table)
183 (modify-syntax-entry ?- "." table)
184 (modify-syntax-entry ?= "." table)
185 (modify-syntax-entry ?* "." table)
186 (modify-syntax-entry ?/ "." table)
187 (modify-syntax-entry ?> "." table)
188 (modify-syntax-entry ?< "." table)
189 (modify-syntax-entry ?& "." table)
190 (modify-syntax-entry ?| "." table)
191 (modify-syntax-entry ?! "." table)
192 (modify-syntax-entry ?\\ "." table)
193 (modify-syntax-entry ?\' "." table)
194 (modify-syntax-entry ?\` "." table)
195 (modify-syntax-entry ?. "." table)
196 (modify-syntax-entry ?\" "\"" table)
197 (modify-syntax-entry ?_ "_" table)
198 ;; The "b" flag only applies to the second letter of the comstart
199 ;; and the first letter of the comend, i.e. the "4b" below is ineffective.
200 ;; If we try to put `b' on the single-line comments, we get a similar
201 ;; problem where the % and # chars appear as first chars of the 2-char
202 ;; comend, so the multi-line ender is also turned into style-b.
203 ;; So we need the new "c" comment style.
204 (modify-syntax-entry ?\% "< 13" table)
205 (modify-syntax-entry ?\# "< 13" table)
206 (modify-syntax-entry ?\{ "(} 2c" table)
207 (modify-syntax-entry ?\} "){ 4c" table)
208 (modify-syntax-entry ?\n ">" table)
209 table)
210 "Syntax table in use in `octave-mode' buffers.")
211
212 (defcustom octave-font-lock-texinfo-comment t
213 "Control whether to highlight the texinfo comment block."
214 :type 'boolean
215 :group 'octave
216 :version "24.4")
217
218 (defcustom octave-blink-matching-block t
219 "Control the blinking of matching Octave block keywords.
220 Non-nil means show matching begin of block when inserting a space,
221 newline or semicolon after an else or end keyword."
222 :type 'boolean
223 :group 'octave)
224
225 (defcustom octave-block-offset 2
226 "Extra indentation applied to statements in Octave block structures."
227 :type 'integer
228 :group 'octave)
229
230 (defvar octave-block-comment-start
231 (concat (make-string 2 octave-comment-char) " ")
232 "String to insert to start a new Octave comment on an empty line.")
233
234 (defcustom octave-continuation-offset 4
235 "Extra indentation applied to Octave continuation lines."
236 :type 'integer
237 :group 'octave)
238
239 (eval-and-compile
240 (defconst octave-continuation-marker-regexp "\\\\\\|\\.\\.\\."))
241
242 (defvar octave-continuation-regexp
243 (concat "[^#%\n]*\\(" octave-continuation-marker-regexp
244 "\\)\\s-*\\(\\s<.*\\)?$"))
245
246 ;; Char \ is considered a bad decision for continuing a line.
247 (defconst octave-continuation-string "..."
248 "Character string used for Octave continuation lines.")
249
250 (defvar octave-mode-imenu-generic-expression
251 (list
252 ;; Functions
253 (list nil octave-function-header-regexp 3))
254 "Imenu expression for Octave mode. See `imenu-generic-expression'.")
255
256 (defcustom octave-mode-hook nil
257 "Hook to be run when Octave mode is started."
258 :type 'hook
259 :group 'octave)
260
261 (defcustom octave-send-show-buffer t
262 "Non-nil means display `inferior-octave-buffer' after sending to it."
263 :type 'boolean
264 :group 'octave)
265
266 (defcustom octave-send-line-auto-forward t
267 "Control auto-forward after sending to the inferior Octave process.
268 Non-nil means always go to the next Octave code line after sending."
269 :type 'boolean
270 :group 'octave)
271
272 (defcustom octave-send-echo-input t
273 "Non-nil means echo input sent to the inferior Octave process."
274 :type 'boolean
275 :group 'octave)
276
277 \f
278 ;;; SMIE indentation
279
280 (require 'smie)
281
282 ;; Use '__operators__' in Octave REPL to get a full list.
283 (defconst octave-operator-table
284 '((assoc ";" "\n") (assoc ",") ; The doc claims they have equal precedence!?
285 (right "=" "+=" "-=" "*=" "/=")
286 (assoc "&&") (assoc "||") ; The doc claims they have equal precedence!?
287 (assoc "&") (assoc "|") ; The doc claims they have equal precedence!?
288 (nonassoc "<" "<=" "==" ">=" ">" "!=" "~=")
289 (nonassoc ":") ;No idea what this is.
290 (assoc "+" "-")
291 (assoc "*" "/" "\\" ".\\" ".*" "./")
292 (nonassoc "'" ".'")
293 (nonassoc "++" "--" "!" "~") ;And unary "+" and "-".
294 (right "^" "**" ".^" ".**")
295 ;; It's not really an operator, but for indentation purposes it
296 ;; could be convenient to treat it as one.
297 (assoc "...")))
298
299 (defconst octave-smie-bnf-table
300 '((atom)
301 ;; We can't distinguish the first element in a sequence with
302 ;; precedence grammars, so we can't distinguish the condition
303 ;; if the `if' from the subsequent body, for example.
304 ;; This has to be done later in the indentation rules.
305 (exp (exp "\n" exp)
306 ;; We need to mention at least one of the operators in this part
307 ;; of the grammar: if the BNF and the operator table have
308 ;; no overlap, SMIE can't know how they relate.
309 (exp ";" exp)
310 ("try" exp "catch" exp "end_try_catch")
311 ("try" exp "catch" exp "end")
312 ("unwind_protect" exp
313 "unwind_protect_cleanup" exp "end_unwind_protect")
314 ("unwind_protect" exp "unwind_protect_cleanup" exp "end")
315 ("for" exp "endfor")
316 ("for" exp "end")
317 ("parfor" exp "endparfor")
318 ("parfor" exp "end")
319 ("do" exp "until" atom)
320 ("while" exp "endwhile")
321 ("while" exp "end")
322 ("if" exp "endif")
323 ("if" exp "else" exp "endif")
324 ("if" exp "elseif" exp "else" exp "endif")
325 ("if" exp "elseif" exp "elseif" exp "else" exp "endif")
326 ("if" exp "elseif" exp "elseif" exp "else" exp "end")
327 ("switch" exp "case" exp "endswitch")
328 ("switch" exp "case" exp "otherwise" exp "endswitch")
329 ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch")
330 ("switch" exp "case" exp "case" exp "otherwise" exp "end")
331 ("function" exp "endfunction")
332 ("function" exp "end")
333 ("enumeration" exp "endenumeration")
334 ("enumeration" exp "end")
335 ("events" exp "endevents")
336 ("events" exp "end")
337 ("methods" exp "endmethods")
338 ("methods" exp "end")
339 ("properties" exp "endproperties")
340 ("properties" exp "end")
341 ("classdef" exp "endclassdef")
342 ("classdef" exp "end"))
343 ;; (fundesc (atom "=" atom))
344 ))
345
346 (defconst octave-smie-grammar
347 (smie-prec2->grammar
348 (smie-merge-prec2s
349 (smie-bnf->prec2 octave-smie-bnf-table
350 '((assoc "\n" ";")))
351
352 (smie-precs->prec2 octave-operator-table))))
353
354 ;; Tokenizing needs to be refined so that ";;" is treated as two
355 ;; tokens and also so as to recognize the \n separator (and
356 ;; corresponding continuation lines).
357
358 (defconst octave-operator-regexp
359 (regexp-opt (apply 'append (mapcar 'cdr octave-operator-table))))
360
361 (defun octave-smie-backward-token ()
362 (let ((pos (point)))
363 (forward-comment (- (point)))
364 (cond
365 ((and (not (eq (char-before) ?\;)) ;Coalesce ";" and "\n".
366 (> pos (line-end-position))
367 (if (looking-back octave-continuation-marker-regexp (- (point) 3))
368 (progn
369 (goto-char (match-beginning 0))
370 (forward-comment (- (point)))
371 nil)
372 t)
373 ;; Ignore it if it's within parentheses.
374 (let ((ppss (syntax-ppss)))
375 (not (and (nth 1 ppss)
376 (eq ?\( (char-after (nth 1 ppss)))))))
377 (skip-chars-forward " \t")
378 ;; Why bother distinguishing \n and ;?
379 ";") ;;"\n"
380 ((and (looking-back octave-operator-regexp (- (point) 3) 'greedy)
381 ;; Don't mistake a string quote for a transpose.
382 (not (looking-back "\\s\"" (1- (point)))))
383 (goto-char (match-beginning 0))
384 (match-string-no-properties 0))
385 (t
386 (smie-default-backward-token)))))
387
388 (defun octave-smie-forward-token ()
389 (skip-chars-forward " \t")
390 (when (looking-at (eval-when-compile
391 (concat "\\(" octave-continuation-marker-regexp
392 "\\)[ \t]*\\($\\|[%#]\\)")))
393 (goto-char (match-end 1))
394 (forward-comment 1))
395 (cond
396 ((and (looking-at "[%#\n]")
397 (not (or (save-excursion (skip-chars-backward " \t")
398 ;; Only add implicit ; when needed.
399 (or (bolp) (eq (char-before) ?\;)))
400 ;; Ignore it if it's within parentheses.
401 (let ((ppss (syntax-ppss)))
402 (and (nth 1 ppss)
403 (eq ?\( (char-after (nth 1 ppss))))))))
404 (if (eolp) (forward-char 1) (forward-comment 1))
405 ;; Why bother distinguishing \n and ;?
406 ";") ;;"\n"
407 ((progn (forward-comment (point-max)) nil))
408 ((looking-at ";[ \t]*\\($\\|[%#]\\)")
409 ;; Combine the ; with the subsequent \n.
410 (goto-char (match-beginning 1))
411 (forward-comment 1)
412 ";")
413 ((and (looking-at octave-operator-regexp)
414 ;; Don't mistake a string quote for a transpose.
415 (not (looking-at "\\s\"")))
416 (goto-char (match-end 0))
417 (match-string-no-properties 0))
418 (t
419 (smie-default-forward-token))))
420
421 (defun octave-smie-rules (kind token)
422 (pcase (cons kind token)
423 ;; We could set smie-indent-basic instead, but that would have two
424 ;; disadvantages:
425 ;; - changes to octave-block-offset wouldn't take effect immediately.
426 ;; - edebug wouldn't show the use of this variable.
427 (`(:elem . basic) octave-block-offset)
428 ;; Since "case" is in the same BNF rules as switch..end, SMIE by default
429 ;; aligns it with "switch".
430 (`(:before . "case") (if (not (smie-rule-sibling-p)) octave-block-offset))
431 (`(:after . ";")
432 (if (smie-rule-parent-p "classdef" "events" "enumeration" "function" "if"
433 "while" "else" "elseif" "for" "parfor"
434 "properties" "methods" "otherwise" "case"
435 "try" "catch" "unwind_protect"
436 "unwind_protect_cleanup")
437 (smie-rule-parent octave-block-offset)
438 ;; For (invalid) code between switch and case.
439 ;; (if (smie-parent-p "switch") 4)
440 nil))))
441
442 (defun octave-indent-comment ()
443 "A function for `smie-indent-functions' (which see)."
444 (save-excursion
445 (back-to-indentation)
446 (cond
447 ((octave-in-string-or-comment-p) nil)
448 ((looking-at-p "\\(\\s<\\)\\1\\{2,\\}")
449 0)
450 ;; Exclude %{, %} and %!.
451 ((and (looking-at-p "\\s<\\(?:[^{}!]\\|$\\)")
452 (not (looking-at-p "\\(\\s<\\)\\1")))
453 (comment-choose-indent)))))
454
455 \f
456 (defvar octave-font-lock-keywords
457 (list
458 ;; Fontify all builtin keywords.
459 (cons (concat "\\_<\\("
460 (regexp-opt octave-reserved-words)
461 "\\)\\_>")
462 'font-lock-keyword-face)
463 ;; Note: 'end' also serves as the last index in an indexing expression.
464 ;; Ref: http://www.mathworks.com/help/matlab/ref/end.html
465 (list (lambda (limit)
466 (while (re-search-forward "\\_<end\\_>" limit 'move)
467 (let ((beg (match-beginning 0))
468 (end (match-end 0)))
469 (unless (octave-in-string-or-comment-p)
470 (condition-case nil
471 (progn
472 (goto-char beg)
473 (backward-up-list)
474 (when (memq (char-after) '(?\( ?\[ ?\{))
475 (put-text-property beg end 'face nil))
476 (goto-char end))
477 (error (goto-char end))))))
478 nil))
479 ;; Fontify all operators.
480 (cons octave-operator-regexp 'font-lock-builtin-face)
481 ;; Fontify all function declarations.
482 (list octave-function-header-regexp
483 '(1 font-lock-keyword-face)
484 '(3 font-lock-function-name-face nil t)))
485 "Additional Octave expressions to highlight.")
486
487 (defun octave-syntax-propertize-function (start end)
488 (goto-char start)
489 (octave-syntax-propertize-sqs end)
490 (funcall (syntax-propertize-rules
491 ("\\\\" (0 (when (eq (nth 3 (save-excursion
492 (syntax-ppss (match-beginning 0))))
493 ?\")
494 (string-to-syntax "\\"))))
495 ;; Try to distinguish the string-quotes from the transpose-quotes.
496 ("\\(?:^\\|[[({,; ]\\)\\('\\)"
497 (1 (prog1 "\"'" (octave-syntax-propertize-sqs end)))))
498 (point) end))
499
500 (defun octave-syntax-propertize-sqs (end)
501 "Propertize the content/end of single-quote strings."
502 (when (eq (nth 3 (syntax-ppss)) ?\')
503 ;; A '..' string.
504 (when (re-search-forward
505 "\\(?:\\=\\|[^']\\)\\(?:''\\)*\\('\\)\\($\\|[^']\\)" end 'move)
506 (goto-char (match-beginning 2))
507 (when (eq (char-before (match-beginning 1)) ?\\)
508 ;; Backslash cannot escape a single quote.
509 (put-text-property (1- (match-beginning 1)) (match-beginning 1)
510 'syntax-table (string-to-syntax ".")))
511 (put-text-property (match-beginning 1) (match-end 1)
512 'syntax-table (string-to-syntax "\"'")))))
513
514 (defvar electric-layout-rules)
515
516 ;;;###autoload
517 (define-derived-mode octave-mode prog-mode "Octave"
518 "Major mode for editing Octave code.
519
520 Octave is a high-level language, primarily intended for numerical
521 computations. It provides a convenient command line interface
522 for solving linear and nonlinear problems numerically. Function
523 definitions can also be stored in files and used in batch mode."
524 :abbrev-table octave-abbrev-table
525
526 (smie-setup octave-smie-grammar #'octave-smie-rules
527 :forward-token #'octave-smie-forward-token
528 :backward-token #'octave-smie-backward-token)
529 (setq-local smie-indent-basic 'octave-block-offset)
530 (add-hook 'smie-indent-functions #'octave-indent-comment nil t)
531
532 (setq-local smie-blink-matching-triggers
533 (cons ?\; smie-blink-matching-triggers))
534 (unless octave-blink-matching-block
535 (remove-hook 'post-self-insert-hook #'smie-blink-matching-open 'local))
536
537 (setq-local electric-indent-chars
538 (cons ?\; electric-indent-chars))
539 ;; IIUC matlab-mode takes the opposite approach: it makes RET insert
540 ;; a ";" at those places where it's correct (i.e. outside of parens).
541 (setq-local electric-layout-rules '((?\; . after)))
542
543 (setq-local comment-use-global-state t)
544 (setq-local comment-start octave-comment-start)
545 (setq-local comment-end "")
546 (setq-local comment-start-skip octave-comment-start-skip)
547 (setq-local comment-add 1)
548
549 (setq-local parse-sexp-ignore-comments t)
550 (setq-local paragraph-start (concat "\\s-*$\\|" page-delimiter))
551 (setq-local paragraph-separate paragraph-start)
552 (setq-local paragraph-ignore-fill-prefix t)
553 (setq-local fill-paragraph-function 'octave-fill-paragraph)
554
555 (setq-local fill-nobreak-predicate
556 (lambda () (eq (octave-in-string-p) ?')))
557 (add-function :around (local 'comment-line-break-function)
558 #'octave--indent-new-comment-line)
559
560 (setq font-lock-defaults '(octave-font-lock-keywords))
561
562 (setq-local syntax-propertize-function #'octave-syntax-propertize-function)
563
564 (setq-local imenu-generic-expression octave-mode-imenu-generic-expression)
565 (setq-local imenu-case-fold-search nil)
566
567 (setq-local add-log-current-defun-function #'octave-add-log-current-defun)
568
569 (add-hook 'completion-at-point-functions 'octave-completion-at-point nil t)
570 (add-hook 'before-save-hook 'octave-sync-function-file-names nil t)
571 (setq-local beginning-of-defun-function 'octave-beginning-of-defun)
572 (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment))
573 (setq-local eldoc-documentation-function 'octave-eldoc-function)
574
575 (easy-menu-add octave-mode-menu))
576
577 \f
578 (defcustom inferior-octave-program "octave"
579 "Program invoked by `inferior-octave'."
580 :type 'string
581 :group 'octave)
582
583 (defcustom inferior-octave-buffer "*Inferior Octave*"
584 "Name of buffer for running an inferior Octave process."
585 :type 'string
586 :group 'octave)
587
588 (defcustom inferior-octave-prompt
589 "\\(^octave\\(\\|.bin\\|.exe\\)\\(-[.0-9]+\\)?\\(:[0-9]+\\)?\\|^debug\\|^\\)>+ "
590 "Regexp to match prompts for the inferior Octave process."
591 :type 'regexp
592 :group 'octave)
593
594 (defcustom inferior-octave-prompt-read-only comint-prompt-read-only
595 "If non-nil, the Octave prompt is read only.
596 See `comint-prompt-read-only' for details."
597 :type 'boolean
598 :group 'octave
599 :version "24.4")
600
601 (defcustom inferior-octave-startup-file
602 (convert-standard-filename
603 (concat "~/.emacs-" (file-name-nondirectory inferior-octave-program)))
604 "Name of the inferior Octave startup file.
605 The contents of this file are sent to the inferior Octave process on
606 startup."
607 :type '(choice (const :tag "None" nil) file)
608 :group 'octave
609 :version "24.4")
610
611 (defcustom inferior-octave-startup-args '("-i" "--no-line-editing")
612 "List of command line arguments for the inferior Octave process.
613 For example, for suppressing the startup message and using `traditional'
614 mode, include \"-q\" and \"--traditional\"."
615 :type '(repeat string)
616 :group 'octave
617 :version "24.4")
618
619 (defcustom inferior-octave-mode-hook nil
620 "Hook to be run when Inferior Octave mode is started."
621 :type 'hook
622 :group 'octave)
623
624 (defvar inferior-octave-process nil)
625
626 (defvar inferior-octave-mode-map
627 (let ((map (make-sparse-keymap)))
628 (set-keymap-parent map comint-mode-map)
629 (define-key map "\M-." 'octave-find-definition)
630 (define-key map "\t" 'completion-at-point)
631 (define-key map "\C-hd" 'octave-help)
632 ;; Same as in `shell-mode'.
633 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
634 (define-key map "\C-c\C-l" 'inferior-octave-dynamic-list-input-ring)
635 (define-key map [menu-bar inout list-history]
636 '("List Input History" . inferior-octave-dynamic-list-input-ring))
637 map)
638 "Keymap used in Inferior Octave mode.")
639
640 (defvar inferior-octave-mode-syntax-table
641 (let ((table (make-syntax-table octave-mode-syntax-table)))
642 table)
643 "Syntax table in use in inferior-octave-mode buffers.")
644
645 (defvar inferior-octave-font-lock-keywords
646 (list
647 (cons inferior-octave-prompt 'font-lock-type-face))
648 ;; Could certainly do more font locking in inferior Octave ...
649 "Additional expressions to highlight in Inferior Octave mode.")
650
651 (defvar inferior-octave-output-list nil)
652 (defvar inferior-octave-output-string nil)
653 (defvar inferior-octave-receive-in-progress nil)
654
655 (define-obsolete-variable-alias 'inferior-octave-startup-hook
656 'inferior-octave-mode-hook "24.4")
657
658 (defvar inferior-octave-dynamic-complete-functions
659 '(inferior-octave-completion-at-point comint-filename-completion)
660 "List of functions called to perform completion for inferior Octave.
661 This variable is used to initialize `comint-dynamic-complete-functions'
662 in the Inferior Octave buffer.")
663
664 (defvar info-lookup-mode)
665
666 (define-derived-mode inferior-octave-mode comint-mode "Inferior Octave"
667 "Major mode for interacting with an inferior Octave process."
668 :abbrev-table octave-abbrev-table
669 (setq comint-prompt-regexp inferior-octave-prompt)
670
671 (setq-local comment-use-global-state t)
672 (setq-local comment-start octave-comment-start)
673 (setq-local comment-end "")
674 (setq comment-column 32)
675 (setq-local comment-start-skip octave-comment-start-skip)
676
677 (setq font-lock-defaults '(inferior-octave-font-lock-keywords nil nil))
678
679 (setq-local info-lookup-mode 'octave-mode)
680 (setq-local eldoc-documentation-function 'octave-eldoc-function)
681
682 (setq comint-input-ring-file-name
683 (or (getenv "OCTAVE_HISTFILE") "~/.octave_hist")
684 comint-input-ring-size (or (getenv "OCTAVE_HISTSIZE") 1024))
685 (setq-local comint-dynamic-complete-functions
686 inferior-octave-dynamic-complete-functions)
687 (setq-local comint-prompt-read-only inferior-octave-prompt-read-only)
688 (add-hook 'comint-input-filter-functions
689 'inferior-octave-directory-tracker nil t)
690 ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572
691 (add-hook 'window-configuration-change-hook
692 'inferior-octave-track-window-width-change nil t)
693 (comint-read-input-ring t))
694
695 ;;;###autoload
696 (defun inferior-octave (&optional arg)
697 "Run an inferior Octave process, I/O via `inferior-octave-buffer'.
698 This buffer is put in Inferior Octave mode. See `inferior-octave-mode'.
699
700 Unless ARG is non-nil, switches to this buffer.
701
702 The elements of the list `inferior-octave-startup-args' are sent as
703 command line arguments to the inferior Octave process on startup.
704
705 Additional commands to be executed on startup can be provided either in
706 the file specified by `inferior-octave-startup-file' or by the default
707 startup file, `~/.emacs-octave'."
708 (interactive "P")
709 (let ((buffer (get-buffer-create inferior-octave-buffer)))
710 (unless arg
711 (pop-to-buffer buffer))
712 (unless (comint-check-proc buffer)
713 (with-current-buffer buffer
714 (inferior-octave-startup)
715 (inferior-octave-mode)))
716 buffer))
717
718 ;;;###autoload
719 (defalias 'run-octave 'inferior-octave)
720
721 (defun inferior-octave-startup ()
722 "Start an inferior Octave process."
723 (let ((proc (comint-exec-1
724 (substring inferior-octave-buffer 1 -1)
725 inferior-octave-buffer
726 inferior-octave-program
727 (append
728 inferior-octave-startup-args
729 ;; --no-gui is introduced in Octave > 3.7
730 (and (not (member "--no-gui" inferior-octave-startup-args))
731 (zerop (process-file inferior-octave-program
732 nil nil nil "--no-gui" "--help"))
733 '("--no-gui"))))))
734 (set-process-filter proc 'inferior-octave-output-digest)
735 (setq inferior-octave-process proc
736 inferior-octave-output-list nil
737 inferior-octave-output-string nil
738 inferior-octave-receive-in-progress t)
739
740 ;; This may look complicated ... However, we need to make sure that
741 ;; we additional startup code only AFTER Octave is ready (otherwise,
742 ;; output may be mixed up). Hence, we need to digest the Octave
743 ;; output to see when it issues a prompt.
744 (while inferior-octave-receive-in-progress
745 (or (process-live-p inferior-octave-process)
746 (error "Process `%s' died" inferior-octave-process))
747 (accept-process-output inferior-octave-process))
748 (goto-char (point-max))
749 (set-marker (process-mark proc) (point))
750 (insert-before-markers
751 (concat
752 (if (not (bobp)) "\f\n")
753 (if inferior-octave-output-list
754 (concat (mapconcat
755 'identity inferior-octave-output-list "\n")
756 "\n"))))
757
758 ;; An empty secondary prompt, as e.g. obtained by '--braindead',
759 ;; means trouble.
760 (inferior-octave-send-list-and-digest (list "PS2\n"))
761 (when (string-match "\\(PS2\\|ans\\) = *$"
762 (car inferior-octave-output-list))
763 (inferior-octave-send-list-and-digest (list "PS2 ('> ');\n")))
764
765 (inferior-octave-send-list-and-digest
766 (list "disp (getenv ('OCTAVE_SRCDIR'))\n"))
767 (process-put proc 'octave-srcdir
768 (unless (equal (car inferior-octave-output-list) "")
769 (car inferior-octave-output-list)))
770
771 ;; O.K., now we are ready for the Inferior Octave startup commands.
772 (inferior-octave-send-list-and-digest
773 (list "more off;\n"
774 (unless (equal inferior-octave-output-string ">> ")
775 "PS1 ('\\s> ');\n")
776 (when (and inferior-octave-startup-file
777 (file-exists-p inferior-octave-startup-file))
778 (format "source ('%s');\n" inferior-octave-startup-file))))
779 (when inferior-octave-output-list
780 (insert-before-markers
781 (mapconcat 'identity inferior-octave-output-list "\n")))
782
783 ;; And finally, everything is back to normal.
784 (set-process-filter proc 'comint-output-filter)
785 ;; Just in case, to be sure a cd in the startup file won't have
786 ;; detrimental effects.
787 (with-demoted-errors (inferior-octave-resync-dirs))
788 ;; Generate a proper prompt, which is critical to
789 ;; `comint-history-isearch-backward-regexp'. Bug#14433.
790 (comint-send-string proc "\n")))
791
792 (defvar inferior-octave-completion-table
793 ;;
794 ;; Use cache to avoid repetitive computation of completions due to
795 ;; bug#11906 - http://debbugs.gnu.org/11906 - which may cause
796 ;; noticeable delay. CACHE: (CMD TIME VALUE).
797 (let ((cache))
798 (completion-table-dynamic
799 (lambda (command)
800 (unless (and (equal (car cache) command)
801 (< (float-time) (+ 5 (cadr cache))))
802 (inferior-octave-send-list-and-digest
803 (list (format "completion_matches ('%s');\n" command)))
804 (setq cache (list command (float-time)
805 (delete-consecutive-dups
806 (sort inferior-octave-output-list 'string-lessp)))))
807 (car (cddr cache))))))
808
809 (defun inferior-octave-completion-at-point ()
810 "Return the data to complete the Octave symbol at point."
811 ;; http://debbugs.gnu.org/14300
812 (let* ((filecomp (string-match-p
813 "/" (or (comint--match-partial-filename) "")))
814 (end (point))
815 (start
816 (unless filecomp
817 (save-excursion
818 (skip-syntax-backward "w_" (comint-line-beginning-position))
819 (point)))))
820 (when (and start (> end start))
821 (list start end (completion-table-in-turn
822 inferior-octave-completion-table
823 'comint-completion-file-name-table)))))
824
825 (define-obsolete-function-alias 'inferior-octave-complete
826 'completion-at-point "24.1")
827
828 (defun inferior-octave-dynamic-list-input-ring ()
829 "List the buffer's input history in a help buffer."
830 ;; We cannot use `comint-dynamic-list-input-ring', because it replaces
831 ;; "completion" by "history reference" ...
832 (interactive)
833 (if (or (not (ring-p comint-input-ring))
834 (ring-empty-p comint-input-ring))
835 (message "No history")
836 (let ((history nil)
837 (history-buffer " *Input History*")
838 (index (1- (ring-length comint-input-ring)))
839 (conf (current-window-configuration)))
840 ;; We have to build up a list ourselves from the ring vector.
841 (while (>= index 0)
842 (setq history (cons (ring-ref comint-input-ring index) history)
843 index (1- index)))
844 ;; Change "completion" to "history reference"
845 ;; to make the display accurate.
846 (with-output-to-temp-buffer history-buffer
847 (display-completion-list history)
848 (set-buffer history-buffer))
849 (message "Hit space to flush")
850 (let ((ch (read-event)))
851 (if (eq ch ?\ )
852 (set-window-configuration conf)
853 (setq unread-command-events (list ch)))))))
854
855 (defun inferior-octave-output-digest (_proc string)
856 "Special output filter for the inferior Octave process.
857 Save all output between newlines into `inferior-octave-output-list', and
858 the rest to `inferior-octave-output-string'."
859 (setq string (concat inferior-octave-output-string string))
860 (while (string-match "\n" string)
861 (setq inferior-octave-output-list
862 (append inferior-octave-output-list
863 (list (substring string 0 (match-beginning 0))))
864 string (substring string (match-end 0))))
865 (if (string-match inferior-octave-prompt string)
866 (setq inferior-octave-receive-in-progress nil))
867 (setq inferior-octave-output-string string))
868
869 (defun inferior-octave-check-process ()
870 (or (and inferior-octave-process
871 (process-live-p inferior-octave-process))
872 (error (substitute-command-keys
873 "No inferior octave process running. Type \\[run-octave]"))))
874
875 (defun inferior-octave-send-list-and-digest (list)
876 "Send LIST to the inferior Octave process and digest the output.
877 The elements of LIST have to be strings and are sent one by one. All
878 output is passed to the filter `inferior-octave-output-digest'."
879 (inferior-octave-check-process)
880 (let* ((proc inferior-octave-process)
881 (filter (process-filter proc))
882 string)
883 (set-process-filter proc 'inferior-octave-output-digest)
884 (setq inferior-octave-output-list nil)
885 (unwind-protect
886 (while (setq string (car list))
887 (setq inferior-octave-output-string nil
888 inferior-octave-receive-in-progress t)
889 (comint-send-string proc string)
890 (while inferior-octave-receive-in-progress
891 (accept-process-output proc))
892 (setq list (cdr list)))
893 (set-process-filter proc filter))))
894
895 (defvar inferior-octave-directory-tracker-resync nil)
896 (make-variable-buffer-local 'inferior-octave-directory-tracker-resync)
897
898 (defun inferior-octave-directory-tracker (string)
899 "Tracks `cd' commands issued to the inferior Octave process.
900 Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
901 (when inferior-octave-directory-tracker-resync
902 (or (inferior-octave-resync-dirs 'noerror)
903 (setq inferior-octave-directory-tracker-resync nil)))
904 (cond
905 ((string-match "^[ \t]*cd[ \t;]*$" string)
906 (cd "~"))
907 ((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
908 (condition-case err
909 (cd (match-string 1 string))
910 (error (setq inferior-octave-directory-tracker-resync t)
911 (message "%s: `%s'"
912 (error-message-string err)
913 (match-string 1 string)))))))
914
915 (defun inferior-octave-resync-dirs (&optional noerror)
916 "Resync the buffer's idea of the current directory.
917 This command queries the inferior Octave process about its current
918 directory and makes this the current buffer's default directory."
919 (interactive)
920 (inferior-octave-send-list-and-digest '("disp (pwd ())\n"))
921 (condition-case err
922 (progn
923 (cd (car inferior-octave-output-list))
924 t)
925 (error (unless noerror (signal (car err) (cdr err))))))
926
927 (defcustom inferior-octave-minimal-columns 80
928 "The minimal column width for the inferior Octave process."
929 :type 'integer
930 :group 'octave
931 :version "24.4")
932
933 (defvar inferior-octave-last-column-width nil)
934
935 (defun inferior-octave-track-window-width-change ()
936 ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572
937 (let ((width (max inferior-octave-minimal-columns (window-width))))
938 (unless (eq inferior-octave-last-column-width width)
939 (setq-local inferior-octave-last-column-width width)
940 (when (and inferior-octave-process
941 (process-live-p inferior-octave-process))
942 (inferior-octave-send-list-and-digest
943 (list (format "putenv ('COLUMNS', '%s');\n" width)))))))
944
945 \f
946 ;;; Miscellaneous useful functions
947
948 (defun octave-in-comment-p ()
949 "Return non-nil if point is inside an Octave comment."
950 (nth 4 (syntax-ppss)))
951
952 (defun octave-in-string-p ()
953 "Return non-nil if point is inside an Octave string."
954 (nth 3 (syntax-ppss)))
955
956 (defun octave-in-string-or-comment-p ()
957 "Return non-nil if point is inside an Octave string or comment."
958 (nth 8 (syntax-ppss)))
959
960 (defun octave-looking-at-kw (regexp)
961 "Like `looking-at', but sets `case-fold-search' nil."
962 (let ((case-fold-search nil))
963 (looking-at regexp)))
964
965 (defun octave-maybe-insert-continuation-string ()
966 (if (or (octave-in-comment-p)
967 (save-excursion
968 (beginning-of-line)
969 (looking-at octave-continuation-regexp)))
970 nil
971 (delete-horizontal-space)
972 (insert (concat " " octave-continuation-string))))
973
974 (defun octave-completing-read ()
975 (let ((def (or (thing-at-point 'symbol)
976 (save-excursion
977 (skip-syntax-backward "-(")
978 (thing-at-point 'symbol)))))
979 (completing-read
980 (format (if def "Function (default %s): "
981 "Function: ") def)
982 inferior-octave-completion-table
983 nil nil nil nil def)))
984
985 (defun octave-goto-function-definition (fn)
986 "Go to the function definition of FN in current buffer."
987 (let ((search
988 (lambda (re sub)
989 (let ((orig (point)) found)
990 (goto-char (point-min))
991 (while (and (not found) (re-search-forward re nil t))
992 (when (and (equal (match-string sub) fn)
993 (not (nth 8 (syntax-ppss))))
994 (setq found t)))
995 (unless found (goto-char orig))
996 found))))
997 (pcase (and buffer-file-name (file-name-extension buffer-file-name))
998 (`"cc" (funcall search
999 "\\_<DEFUN\\(?:_DLD\\)?\\s-*(\\s-*\\(\\(?:\\sw\\|\\s_\\)+\\)" 1))
1000 (t (funcall search octave-function-header-regexp 3)))))
1001
1002 (defun octave-function-file-p ()
1003 "Return non-nil if the first token is \"function\".
1004 The value is (START END NAME-START NAME-END) of the function."
1005 (save-excursion
1006 (goto-char (point-min))
1007 (when (equal (funcall smie-forward-token-function) "function")
1008 (forward-word -1)
1009 (let* ((start (point))
1010 (end (progn (forward-sexp 1) (point)))
1011 (name (when (progn
1012 (goto-char start)
1013 (re-search-forward octave-function-header-regexp
1014 end t))
1015 (list (match-beginning 3) (match-end 3)))))
1016 (cons start (cons end name))))))
1017
1018 ;; Like forward-comment but stop at non-comment blank
1019 (defun octave-skip-comment-forward (limit)
1020 (let ((ppss (syntax-ppss)))
1021 (if (nth 4 ppss)
1022 (goto-char (nth 8 ppss))
1023 (goto-char (or (comment-search-forward limit t) (point)))))
1024 (while (and (< (point) limit) (looking-at-p "\\s<"))
1025 (forward-comment 1)))
1026
1027 ;;; First non-copyright comment block
1028 (defun octave-function-file-comment ()
1029 "Beginning and end positions of the function file comment."
1030 (save-excursion
1031 (goto-char (point-min))
1032 ;; Copyright block: octave/libinterp/parse-tree/lex.ll around line 1634
1033 (while (save-excursion
1034 (when (comment-search-forward (point-max) t)
1035 (when (eq (char-after) ?\{) ; case of block comment
1036 (forward-char 1))
1037 (skip-syntax-forward "-")
1038 (let ((case-fold-search t))
1039 (looking-at-p "\\(?:copyright\\|author\\)\\_>"))))
1040 (octave-skip-comment-forward (point-max)))
1041 (let ((beg (comment-search-forward (point-max) t)))
1042 (when beg
1043 (goto-char beg)
1044 (octave-skip-comment-forward (point-max))
1045 (list beg (point))))))
1046
1047 (defun octave-sync-function-file-names ()
1048 "Ensure function name agree with function file name.
1049 See Info node `(octave)Function Files'."
1050 (interactive)
1051 (when buffer-file-name
1052 (pcase-let ((`(,start ,_end ,name-start ,name-end)
1053 (octave-function-file-p)))
1054 (when (and start name-start)
1055 (let* ((func (buffer-substring name-start name-end))
1056 (file (file-name-sans-extension
1057 (file-name-nondirectory buffer-file-name)))
1058 (help-form (format "\
1059 a: Use function name `%s'
1060 b: Use file name `%s'
1061 q: Don't fix\n" func file))
1062 (c (unless (equal file func)
1063 (save-window-excursion
1064 (help-form-show)
1065 (read-char-choice
1066 "Which name to use? (a/b/q) " '(?a ?b ?q))))))
1067 (pcase c
1068 (`?a (let ((newname (expand-file-name
1069 (concat func (file-name-extension
1070 buffer-file-name t)))))
1071 (when (or (not (file-exists-p newname))
1072 (yes-or-no-p
1073 (format "Target file %s exists; proceed? " newname)))
1074 (when (file-exists-p buffer-file-name)
1075 (rename-file buffer-file-name newname t))
1076 (set-visited-file-name newname))))
1077 (`?b (save-excursion
1078 (goto-char name-start)
1079 (delete-region name-start name-end)
1080 (insert file)))))))))
1081
1082 (defun octave-update-function-file-comment (beg end)
1083 "Query replace function names in function file comment."
1084 (interactive
1085 (progn
1086 (barf-if-buffer-read-only)
1087 (if (use-region-p)
1088 (list (region-beginning) (region-end))
1089 (or (octave-function-file-comment)
1090 (error "No function file comment found")))))
1091 (save-excursion
1092 (let* ((bounds (or (octave-function-file-p)
1093 (error "Not in a function file buffer")))
1094 (func (if (cddr bounds)
1095 (apply #'buffer-substring (cddr bounds))
1096 (error "Function name not found")))
1097 (old-func (progn
1098 (goto-char beg)
1099 (when (re-search-forward
1100 "[=}]\\s-*\\(\\(?:\\sw\\|\\s_\\)+\\)\\_>"
1101 (min (line-end-position 4) end)
1102 t)
1103 (match-string 1))))
1104 (old-func (read-string (format (if old-func
1105 "Name to replace (default %s): "
1106 "Name to replace: ")
1107 old-func)
1108 nil nil old-func)))
1109 (if (and func old-func (not (equal func old-func)))
1110 (perform-replace old-func func 'query
1111 nil 'delimited nil nil beg end)
1112 (message "Function names match")))))
1113
1114 (defface octave-function-comment-block
1115 '((t (:inherit font-lock-doc-face)))
1116 "Face used to highlight function comment block."
1117 :group 'octave)
1118
1119 (eval-when-compile (require 'texinfo))
1120
1121 (defun octave-font-lock-texinfo-comment ()
1122 (let ((kws
1123 (eval-when-compile
1124 (delq nil (mapcar
1125 (lambda (kw)
1126 (if (numberp (nth 1 kw))
1127 `(,(nth 0 kw) ,(nth 1 kw) ,(nth 2 kw) prepend)
1128 (message "Ignoring Texinfo highlight: %S" kw)))
1129 texinfo-font-lock-keywords)))))
1130 (font-lock-add-keywords
1131 nil
1132 `((,(lambda (limit)
1133 (while (and (< (point) limit)
1134 (search-forward "-*- texinfo -*-" limit t)
1135 (octave-in-comment-p))
1136 (let ((beg (nth 8 (syntax-ppss)))
1137 (end (progn
1138 (octave-skip-comment-forward (point-max))
1139 (point))))
1140 (put-text-property beg end 'font-lock-multiline t)
1141 (font-lock-prepend-text-property
1142 beg end 'face 'octave-function-comment-block)
1143 (dolist (kw kws)
1144 (goto-char beg)
1145 (while (re-search-forward (car kw) end 'move)
1146 (font-lock-apply-highlight (cdr kw))))))
1147 nil)))
1148 'append)))
1149
1150 \f
1151 ;;; Indentation
1152
1153 (defun octave-indent-new-comment-line (&optional soft)
1154 ;; FIXME: C-M-j should probably be bound globally to a function like
1155 ;; this one.
1156 "Break Octave line at point, continuing comment if within one.
1157 Insert `octave-continuation-string' before breaking the line
1158 unless inside a list. Signal an error if within a single-quoted
1159 string."
1160 (interactive)
1161 (funcall comment-line-break-function soft))
1162
1163 (defun octave--indent-new-comment-line (orig &rest args)
1164 (cond
1165 ((octave-in-comment-p) nil)
1166 ((eq (octave-in-string-p) ?')
1167 (error "Cannot split a single-quoted string"))
1168 ((eq (octave-in-string-p) ?\")
1169 (insert octave-continuation-string))
1170 (t
1171 (delete-horizontal-space)
1172 (unless (and (cadr (syntax-ppss))
1173 (eq (char-after (cadr (syntax-ppss))) ?\())
1174 (insert " " octave-continuation-string))))
1175 (apply orig args)
1176 (indent-according-to-mode))
1177
1178 (define-obsolete-function-alias
1179 'octave-indent-defun 'prog-indent-sexp "24.4")
1180
1181 \f
1182 ;;; Motion
1183 (defun octave-next-code-line (&optional arg)
1184 "Move ARG lines of Octave code forward (backward if ARG is negative).
1185 Skips past all empty and comment lines. Default for ARG is 1.
1186
1187 On success, return 0. Otherwise, go as far as possible and return -1."
1188 (interactive "p")
1189 (or arg (setq arg 1))
1190 (beginning-of-line)
1191 (let ((n 0)
1192 (inc (if (> arg 0) 1 -1)))
1193 (while (and (/= arg 0) (= n 0))
1194 (setq n (forward-line inc))
1195 (while (and (= n 0)
1196 (looking-at "\\s-*\\($\\|\\s<\\)"))
1197 (setq n (forward-line inc)))
1198 (setq arg (- arg inc)))
1199 n))
1200
1201 (defun octave-previous-code-line (&optional arg)
1202 "Move ARG lines of Octave code backward (forward if ARG is negative).
1203 Skips past all empty and comment lines. Default for ARG is 1.
1204
1205 On success, return 0. Otherwise, go as far as possible and return -1."
1206 (interactive "p")
1207 (or arg (setq arg 1))
1208 (octave-next-code-line (- arg)))
1209
1210 (defun octave-beginning-of-line ()
1211 "Move point to beginning of current Octave line.
1212 If on an empty or comment line, go to the beginning of that line.
1213 Otherwise, move backward to the beginning of the first Octave code line
1214 which is not inside a continuation statement, i.e., which does not
1215 follow a code line ending with `...' or is inside an open
1216 parenthesis list."
1217 (interactive)
1218 (beginning-of-line)
1219 (unless (looking-at "\\s-*\\($\\|\\s<\\)")
1220 (while (or (when (cadr (syntax-ppss))
1221 (goto-char (cadr (syntax-ppss)))
1222 (beginning-of-line)
1223 t)
1224 (and (or (looking-at "\\s-*\\($\\|\\s<\\)")
1225 (save-excursion
1226 (if (zerop (octave-previous-code-line))
1227 (looking-at octave-continuation-regexp))))
1228 (zerop (forward-line -1)))))))
1229
1230 (defun octave-end-of-line ()
1231 "Move point to end of current Octave line.
1232 If on an empty or comment line, go to the end of that line.
1233 Otherwise, move forward to the end of the first Octave code line which
1234 does not end with `...' or is inside an open parenthesis list."
1235 (interactive)
1236 (end-of-line)
1237 (unless (save-excursion
1238 (beginning-of-line)
1239 (looking-at "\\s-*\\($\\|\\s<\\)"))
1240 (while (or (when (cadr (syntax-ppss))
1241 (condition-case nil
1242 (progn
1243 (up-list 1)
1244 (end-of-line)
1245 t)
1246 (error nil)))
1247 (and (save-excursion
1248 (beginning-of-line)
1249 (or (looking-at "\\s-*\\($\\|\\s<\\)")
1250 (looking-at octave-continuation-regexp)))
1251 (zerop (forward-line 1)))))
1252 (end-of-line)))
1253
1254 (defun octave-mark-block ()
1255 "Put point at the beginning of this Octave block, mark at the end.
1256 The block marked is the one that contains point or follows point."
1257 (interactive)
1258 (if (and (looking-at "\\sw\\|\\s_")
1259 (looking-back "\\sw\\|\\s_" (1- (point))))
1260 (skip-syntax-forward "w_"))
1261 (unless (or (looking-at "\\s(")
1262 (save-excursion
1263 (let* ((token (funcall smie-forward-token-function))
1264 (level (assoc token smie-grammar)))
1265 (and level (not (numberp (cadr level)))))))
1266 (backward-up-list 1))
1267 (mark-sexp))
1268
1269 (defun octave-beginning-of-defun (&optional arg)
1270 "Octave-specific `beginning-of-defun-function' (which see)."
1271 (or arg (setq arg 1))
1272 ;; Move out of strings or comments.
1273 (when (octave-in-string-or-comment-p)
1274 (goto-char (octave-in-string-or-comment-p)))
1275 (letrec ((orig (point))
1276 (toplevel (lambda (pos)
1277 (condition-case nil
1278 (progn
1279 (backward-up-list 1)
1280 (funcall toplevel (point)))
1281 (scan-error pos)))))
1282 (goto-char (funcall toplevel (point)))
1283 (when (and (> arg 0) (/= orig (point)))
1284 (setq arg (1- arg)))
1285 (forward-sexp (- arg))
1286 (and (< arg 0) (forward-sexp -1))
1287 (/= orig (point))))
1288
1289 (defun octave-fill-paragraph (&optional _arg)
1290 "Fill paragraph of Octave code, handling Octave comments."
1291 ;; FIXME: difference with generic fill-paragraph:
1292 ;; - code lines are only split, never joined.
1293 ;; - \n that end comments are never removed.
1294 ;; - insert continuation marker when splitting code lines.
1295 (interactive "P")
1296 (save-excursion
1297 (let ((end (progn (forward-paragraph) (copy-marker (point) t)))
1298 (beg (progn
1299 (forward-paragraph -1)
1300 (skip-chars-forward " \t\n")
1301 (beginning-of-line)
1302 (point)))
1303 (cfc (current-fill-column))
1304 comment-prefix)
1305 (goto-char beg)
1306 (while (< (point) end)
1307 (condition-case nil
1308 (indent-according-to-mode)
1309 (error nil))
1310 (move-to-column cfc)
1311 ;; First check whether we need to combine non-empty comment lines
1312 (if (and (< (current-column) cfc)
1313 (octave-in-comment-p)
1314 (not (save-excursion
1315 (beginning-of-line)
1316 (looking-at "^\\s-*\\s<+\\s-*$"))))
1317 ;; This is a nonempty comment line which does not extend
1318 ;; past the fill column. If it is followed by a nonempty
1319 ;; comment line with the same comment prefix, try to
1320 ;; combine them, and repeat this until either we reach the
1321 ;; fill-column or there is nothing more to combine.
1322 (progn
1323 ;; Get the comment prefix
1324 (save-excursion
1325 (beginning-of-line)
1326 (while (and (re-search-forward "\\s<+")
1327 (not (octave-in-comment-p))))
1328 (setq comment-prefix (match-string 0)))
1329 ;; And keep combining ...
1330 (while (and (< (current-column) cfc)
1331 (save-excursion
1332 (forward-line 1)
1333 (and (looking-at
1334 (concat "^\\s-*"
1335 comment-prefix
1336 "\\S<"))
1337 (not (looking-at
1338 (concat "^\\s-*"
1339 comment-prefix
1340 "\\s-*$"))))))
1341 (delete-char 1)
1342 (re-search-forward comment-prefix)
1343 (delete-region (match-beginning 0) (match-end 0))
1344 (fixup-whitespace)
1345 (move-to-column cfc))))
1346 ;; We might also try to combine continued code lines> Perhaps
1347 ;; some other time ...
1348 (skip-chars-forward "^ \t\n")
1349 (delete-horizontal-space)
1350 (if (or (< (current-column) cfc)
1351 (and (= (current-column) cfc) (eolp)))
1352 (forward-line 1)
1353 (if (not (eolp)) (insert " "))
1354 (or (funcall normal-auto-fill-function)
1355 (forward-line 1))))
1356 t)))
1357
1358 (defun octave-completion-at-point ()
1359 "Find the text to complete and the corresponding table."
1360 (let* ((beg (save-excursion (skip-syntax-backward "w_") (point)))
1361 (end (point)))
1362 (if (< beg (point))
1363 ;; Extend region past point, if applicable.
1364 (save-excursion (skip-syntax-forward "w_")
1365 (setq end (point))))
1366 (when (> end beg)
1367 (list beg end (or (and inferior-octave-process
1368 (process-live-p inferior-octave-process)
1369 inferior-octave-completion-table)
1370 octave-reserved-words)))))
1371
1372 (define-obsolete-function-alias 'octave-complete-symbol
1373 'completion-at-point "24.1")
1374
1375 (defun octave-add-log-current-defun ()
1376 "A function for `add-log-current-defun-function' (which see)."
1377 (save-excursion
1378 (end-of-line)
1379 (and (beginning-of-defun)
1380 (re-search-forward octave-function-header-regexp
1381 (line-end-position) t)
1382 (match-string 3))))
1383
1384 \f
1385 ;;; Electric characters && friends
1386 (define-skeleton octave-insert-defun
1387 "Insert an Octave function skeleton.
1388 Prompt for the function's name, arguments and return values (to be
1389 entered without parens)."
1390 (let* ((defname (file-name-sans-extension (buffer-name)))
1391 (name (read-string (format "Function name (default %s): " defname)
1392 nil nil defname))
1393 (args (read-string "Arguments: "))
1394 (vals (read-string "Return values: ")))
1395 (format "%s%s (%s)"
1396 (cond
1397 ((string-equal vals "") vals)
1398 ((string-match "[ ,]" vals) (concat "[" vals "] = "))
1399 (t (concat vals " = ")))
1400 name
1401 args))
1402 \n octave-block-comment-start "usage: " str \n
1403 octave-block-comment-start '(delete-horizontal-space) \n
1404 octave-block-comment-start '(delete-horizontal-space) \n
1405 "function " > str \n
1406 _ \n
1407 "endfunction" > \n)
1408
1409 ;;; Communication with the inferior Octave process
1410 (defun octave-kill-process ()
1411 "Kill inferior Octave process and its buffer."
1412 (interactive)
1413 (if inferior-octave-process
1414 (progn
1415 (process-send-string inferior-octave-process "quit;\n")
1416 (accept-process-output inferior-octave-process)))
1417 (if inferior-octave-buffer
1418 (kill-buffer inferior-octave-buffer)))
1419
1420 (defun octave-show-process-buffer ()
1421 "Make sure that `inferior-octave-buffer' is displayed."
1422 (interactive)
1423 (if (get-buffer inferior-octave-buffer)
1424 (display-buffer inferior-octave-buffer)
1425 (message "No buffer named %s" inferior-octave-buffer)))
1426
1427 (defun octave-hide-process-buffer ()
1428 "Delete all windows that display `inferior-octave-buffer'."
1429 (interactive)
1430 (if (get-buffer inferior-octave-buffer)
1431 (delete-windows-on inferior-octave-buffer)
1432 (message "No buffer named %s" inferior-octave-buffer)))
1433
1434 (defun octave-send-region (beg end)
1435 "Send current region to the inferior Octave process."
1436 (interactive "r")
1437 (inferior-octave t)
1438 (let ((proc inferior-octave-process)
1439 (string (buffer-substring-no-properties beg end))
1440 line)
1441 (with-current-buffer inferior-octave-buffer
1442 (setq inferior-octave-output-list nil)
1443 (while (not (string-equal string ""))
1444 (if (string-match "\n" string)
1445 (setq line (substring string 0 (match-beginning 0))
1446 string (substring string (match-end 0)))
1447 (setq line string string ""))
1448 (setq inferior-octave-receive-in-progress t)
1449 (inferior-octave-send-list-and-digest (list (concat line "\n")))
1450 (while inferior-octave-receive-in-progress
1451 (accept-process-output proc))
1452 (insert-before-markers
1453 (mapconcat 'identity
1454 (append
1455 (if octave-send-echo-input (list line) (list ""))
1456 inferior-octave-output-list
1457 (list inferior-octave-output-string))
1458 "\n")))))
1459 (if octave-send-show-buffer
1460 (display-buffer inferior-octave-buffer)))
1461
1462 (defun octave-send-block ()
1463 "Send current Octave block to the inferior Octave process."
1464 (interactive)
1465 (save-excursion
1466 (octave-mark-block)
1467 (octave-send-region (point) (mark))))
1468
1469 (defun octave-send-defun ()
1470 "Send current Octave function to the inferior Octave process."
1471 (interactive)
1472 (save-excursion
1473 (mark-defun)
1474 (octave-send-region (point) (mark))))
1475
1476 (defun octave-send-line (&optional arg)
1477 "Send current Octave code line to the inferior Octave process.
1478 With positive prefix ARG, send that many lines.
1479 If `octave-send-line-auto-forward' is non-nil, go to the next unsent
1480 code line."
1481 (interactive "P")
1482 (or arg (setq arg 1))
1483 (if (> arg 0)
1484 (let (beg end)
1485 (beginning-of-line)
1486 (setq beg (point))
1487 (octave-next-code-line (- arg 1))
1488 (end-of-line)
1489 (setq end (point))
1490 (if octave-send-line-auto-forward
1491 (octave-next-code-line 1))
1492 (octave-send-region beg end))))
1493
1494 (defun octave-eval-print-last-sexp ()
1495 "Evaluate Octave sexp before point and print value into current buffer."
1496 (interactive)
1497 (inferior-octave t)
1498 (let ((standard-output (current-buffer))
1499 (print-escape-newlines nil)
1500 (opoint (point)))
1501 (terpri)
1502 (prin1
1503 (save-excursion
1504 (forward-sexp -1)
1505 (inferior-octave-send-list-and-digest
1506 (list (concat (buffer-substring-no-properties (point) opoint)
1507 "\n")))
1508 (mapconcat 'identity inferior-octave-output-list "\n")))
1509 (terpri)))
1510
1511 \f
1512
1513 (defcustom octave-eldoc-message-style 'auto
1514 "Octave eldoc message style: auto, oneline, multiline."
1515 :type '(choice (const :tag "Automatic" auto)
1516 (const :tag "One Line" oneline)
1517 (const :tag "Multi Line" multiline))
1518 :group 'octave
1519 :version "24.4")
1520
1521 ;; (FN SIGNATURE1 SIGNATURE2 ...)
1522 (defvar octave-eldoc-cache nil)
1523
1524 (defun octave-eldoc-function-signatures (fn)
1525 (unless (equal fn (car octave-eldoc-cache))
1526 (inferior-octave-send-list-and-digest
1527 (list (format "print_usage ('%s');\n" fn)))
1528 (let (result)
1529 (dolist (line inferior-octave-output-list)
1530 (when (string-match
1531 "\\s-*\\(?:--[^:]+\\|usage\\):\\s-*\\(.*\\)$"
1532 line)
1533 (push (match-string 1 line) result)))
1534 (setq octave-eldoc-cache
1535 (cons (substring-no-properties fn)
1536 (nreverse result)))))
1537 (cdr octave-eldoc-cache))
1538
1539 (defun octave-eldoc-function ()
1540 "A function for `eldoc-documentation-function' (which see)."
1541 (when (and inferior-octave-process
1542 (process-live-p inferior-octave-process))
1543 (let* ((ppss (syntax-ppss))
1544 (paren-pos (cadr ppss))
1545 (fn (save-excursion
1546 (if (and paren-pos
1547 ;; PAREN-POS must be after the prompt
1548 (>= paren-pos
1549 (if (eq (get-buffer-process (current-buffer))
1550 inferior-octave-process)
1551 (process-mark inferior-octave-process)
1552 (point-min)))
1553 (or (not (eq (get-buffer-process (current-buffer))
1554 inferior-octave-process))
1555 (< (process-mark inferior-octave-process)
1556 paren-pos))
1557 (eq (char-after paren-pos) ?\())
1558 (goto-char paren-pos)
1559 (setq paren-pos nil))
1560 (when (or (< (skip-syntax-backward "-") 0) paren-pos)
1561 (thing-at-point 'symbol))))
1562 (sigs (and fn (octave-eldoc-function-signatures fn)))
1563 (oneline (mapconcat 'identity sigs
1564 (propertize " | " 'face 'warning)))
1565 (multiline (mapconcat (lambda (s) (concat "-- " s)) sigs "\n")))
1566 ;;
1567 ;; Return the value according to style.
1568 (pcase octave-eldoc-message-style
1569 (`auto (if (< (length oneline) (window-width (minibuffer-window)))
1570 oneline
1571 multiline))
1572 (`oneline oneline)
1573 (`multiline multiline)))))
1574
1575 (defcustom octave-help-buffer "*Octave Help*"
1576 "Buffer name for `octave-help'."
1577 :type 'string
1578 :group 'octave
1579 :version "24.4")
1580
1581 (define-button-type 'octave-help-file
1582 'follow-link t
1583 'action #'help-button-action
1584 'help-function 'octave-find-definition)
1585
1586 (define-button-type 'octave-help-function
1587 'follow-link t
1588 'action (lambda (b)
1589 (octave-help
1590 (buffer-substring (button-start b) (button-end b)))))
1591
1592 (defvar octave-help-mode-map
1593 (let ((map (make-sparse-keymap)))
1594 (define-key map "\M-." 'octave-find-definition)
1595 (define-key map "\C-hd" 'octave-help)
1596 map))
1597
1598 (define-derived-mode octave-help-mode help-mode "OctHelp"
1599 "Major mode for displaying Octave documentation."
1600 :abbrev-table nil
1601 :syntax-table octave-mode-syntax-table
1602 (eval-and-compile (require 'help-mode))
1603 ;; Mostly stolen from `help-make-xrefs'.
1604 (let ((inhibit-read-only t))
1605 (setq-local info-lookup-mode 'octave-mode)
1606 ;; Delete extraneous newlines at the end of the docstring
1607 (goto-char (point-max))
1608 (while (and (not (bobp)) (bolp))
1609 (delete-char -1))
1610 (insert "\n")
1611 (when (or help-xref-stack help-xref-forward-stack)
1612 (insert "\n"))
1613 (when help-xref-stack
1614 (help-insert-xref-button help-back-label 'help-back
1615 (current-buffer)))
1616 (when help-xref-forward-stack
1617 (when help-xref-stack
1618 (insert "\t"))
1619 (help-insert-xref-button help-forward-label 'help-forward
1620 (current-buffer)))
1621 (when (or help-xref-stack help-xref-forward-stack)
1622 (insert "\n"))))
1623
1624 (defun octave-help (fn)
1625 "Display the documentation of FN."
1626 (interactive (list (octave-completing-read)))
1627 (inferior-octave-send-list-and-digest
1628 (list (format "help ('%s');\n" fn)))
1629 (let ((lines inferior-octave-output-list)
1630 (inhibit-read-only t))
1631 (when (string-match "error: \\(.*\\)$" (car lines))
1632 (error "%s" (match-string 1 (car lines))))
1633 (with-help-window octave-help-buffer
1634 (princ (mapconcat 'identity lines "\n"))
1635 (with-current-buffer octave-help-buffer
1636 ;; Bound to t so that `help-buffer' returns current buffer for
1637 ;; `help-setup-xref'.
1638 (let ((help-xref-following t))
1639 (help-setup-xref (list 'octave-help fn)
1640 (called-interactively-p 'interactive)))
1641 ;; Note: can be turned off by suppress_verbose_help_message.
1642 ;;
1643 ;; Remove boring trailing text: Additional help for built-in functions
1644 ;; and operators ...
1645 (goto-char (point-max))
1646 (when (search-backward "\n\n\n" nil t)
1647 (goto-char (match-beginning 0))
1648 (delete-region (point) (point-max)))
1649 ;; File name highlight
1650 (goto-char (point-min))
1651 (when (re-search-forward "from the file \\(.*\\)$"
1652 (line-end-position)
1653 t)
1654 (let* ((file (match-string 1))
1655 (dir (file-name-directory
1656 (directory-file-name (file-name-directory file)))))
1657 (replace-match "" nil nil nil 1)
1658 (insert "`")
1659 ;; Include the parent directory which may be regarded as
1660 ;; the category for the FN.
1661 (help-insert-xref-button (file-relative-name file dir)
1662 'octave-help-file fn)
1663 (insert "'")))
1664 ;; Make 'See also' clickable.
1665 (with-syntax-table octave-mode-syntax-table
1666 (when (re-search-forward "^\\s-*See also:" nil t)
1667 (let ((end (save-excursion (re-search-forward "^\\s-*$" nil t))))
1668 (while (re-search-forward
1669 ;; Match operators and symbols.
1670 "\\(?1:\\s.+?\\)\\(?:$\\|[,;]\\|\\s-\\)\\|\\_<\\(?1:\\(?:\\sw\\|\\s_\\)+\\)\\_>"
1671 end t)
1672 (make-text-button (match-beginning 1) (match-end 1)
1673 :type 'octave-help-function)))))
1674 (octave-help-mode)))))
1675
1676 (defcustom octave-source-directories nil
1677 "A list of directories for Octave sources.
1678 If the environment variable OCTAVE_SRCDIR is set, it is searched first."
1679 :type '(repeat directory)
1680 :group 'octave
1681 :version "24.4")
1682
1683 (defun octave-source-directories ()
1684 (let ((srcdir (or (and inferior-octave-process
1685 (process-get inferior-octave-process 'octave-srcdir))
1686 (getenv "OCTAVE_SRCDIR"))))
1687 (if srcdir
1688 (cons srcdir octave-source-directories)
1689 octave-source-directories)))
1690
1691 (defvar octave-find-definition-filename-function
1692 #'octave-find-definition-default-filename)
1693
1694 (defun octave-find-definition-default-filename (name)
1695 "Default value for `octave-find-definition-filename-function'."
1696 (pcase (file-name-extension name)
1697 (`"oct"
1698 (octave-find-definition-default-filename
1699 (concat "libinterp/dldfcn/"
1700 (file-name-sans-extension (file-name-nondirectory name))
1701 ".cc")))
1702 (`"cc"
1703 (let ((file (or (locate-file name (octave-source-directories))
1704 (locate-file (file-name-nondirectory name)
1705 (octave-source-directories)))))
1706 (or (and file (file-exists-p file))
1707 (error "File `%s' not found" name))
1708 file))
1709 (`"mex"
1710 (if (yes-or-no-p (format "File `%s' may be binary; open? "
1711 (file-name-nondirectory name)))
1712 name
1713 (user-error "Aborted")))
1714 (t name)))
1715
1716 (defvar find-tag-marker-ring)
1717
1718 (defun octave-find-definition (fn)
1719 "Find the definition of FN.
1720 Functions implemented in C++ can be found if
1721 `octave-source-directories' is set correctly."
1722 (interactive (list (octave-completing-read)))
1723 (require 'etags)
1724 (let ((orig (point)))
1725 (if (and (derived-mode-p 'octave-mode)
1726 (octave-goto-function-definition fn))
1727 (ring-insert find-tag-marker-ring (copy-marker orig))
1728 (inferior-octave-send-list-and-digest
1729 ;; help NAME is more verbose
1730 (list (format "\
1731 if iskeyword('%s') disp('`%s'' is a keyword') else which('%s') endif\n"
1732 fn fn fn)))
1733 (let (line file)
1734 ;; Skip garbage lines such as
1735 ;; warning: fmincg.m: possible Matlab-style ....
1736 (while (and (not file) (consp inferior-octave-output-list))
1737 (setq line (pop inferior-octave-output-list))
1738 (when (string-match "from the file \\(.*\\)$" line)
1739 (setq file (match-string 1 line))))
1740 (if (not file)
1741 (user-error "%s" (or line (format "`%s' not found" fn)))
1742 (ring-insert find-tag-marker-ring (point-marker))
1743 (setq file (funcall octave-find-definition-filename-function file))
1744 (when file
1745 (find-file file)
1746 (octave-goto-function-definition fn)))))))
1747
1748 (provide 'octave)
1749 ;;; octave.el ends here