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