(easy-menu-define octave-mode-menu octave-mode-map
"Menu for Octave mode."
'("Octave"
- ("Lines"
- ["Previous Code Line" octave-previous-code-line t]
- ["Next Code Line" octave-next-code-line t]
- ["Begin of Continuation" octave-beginning-of-line t]
- ["End of Continuation" octave-end-of-line t]
- ["Split Line at Point" octave-indent-new-comment-line t])
- ("Blocks"
- ["Mark Block" octave-mark-block t]
- ["Close Block" smie-close-block t])
- ("Functions"
- ["Insert Function" octave-insert-defun t]
- ["Update function file comment" octave-update-function-file-comment t])
- "-"
+ ["Split Line at Point" octave-indent-new-comment-line t]
+ ["Previous Code Line" octave-previous-code-line t]
+ ["Next Code Line" octave-next-code-line t]
+ ["Begin of Line" octave-beginning-of-line t]
+ ["End of Line" octave-end-of-line t]
+ ["Mark Block" octave-mark-block t]
+ ["Close Block" smie-close-block t]
+ "---"
+ ["Start Octave Process" run-octave t]
+ ["Documentation Lookup" info-lookup-symbol t]
+ ["Help on Function" octave-help t]
+ ["Find Function Definition" octave-find-definition t]
+ ["Insert Function" octave-insert-defun t]
+ ["Update Function File Comment" octave-update-function-file-comment t]
+ "---"
+ ["Function Syntax Hints" (call-interactively
+ (if (fboundp 'eldoc-post-insert-mode)
+ 'eldoc-post-insert-mode
+ 'eldoc-mode))
+ :style toggle :selected (or eldoc-post-insert-mode eldoc-mode)
+ :help "Display function signatures after typing `SPC' or `('"]
+ ["Delimiter Matching" smie-highlight-matching-block-mode
+ :style toggle :selected smie-highlight-matching-block-mode
+ :help "Highlight matched pairs such as `if ... end'"
+ :visible (fboundp 'smie-highlight-matching-block-mode)]
+ ["Auto Fill" auto-fill-mode
+ :style toggle :selected auto-fill-function
+ :help "Automatic line breaking"]
+ ["Electric Layout" electric-layout-mode
+ :style toggle :selected electric-layout-mode
+ :help "Automatically insert newlines around some chars"]
+ "---"
("Debug"
- ["Send Current Line" octave-send-line t]
- ["Send Current Block" octave-send-block t]
- ["Send Current Function" octave-send-defun t]
- ["Send Region" octave-send-region t]
- ["Show Process Buffer" octave-show-process-buffer t]
- ["Hide Process Buffer" octave-hide-process-buffer t]
- ["Kill Process" octave-kill-process t])
- "-"
- ["Indent Line" indent-according-to-mode t]
- ["Complete Symbol" completion-at-point t]
- ["Toggle Auto-Fill Mode" auto-fill-mode
- :style toggle :selected auto-fill-function]
- "-"
- ["Describe Octave Mode" describe-mode t]
- ["Lookup Octave Index" info-lookup-symbol t]
- ["Customize Octave" (customize-group 'octave) t]
- "-"
- ["Submit Bug Report" report-emacs-bug t]))
+ ["Send Current Line" octave-send-line t]
+ ["Send Current Block" octave-send-block t]
+ ["Send Current Function" octave-send-defun t]
+ ["Send Region" octave-send-region t]
+ ["Show Process Buffer" octave-show-process-buffer t]
+ ["Hide Process Buffer" octave-hide-process-buffer t]
+ ["Kill Process" octave-kill-process t])
+ "---"
+ ["Customize Octave" (customize-group 'octave) t]
+ ["Submit Bug Report" report-emacs-bug t]))
(defvar octave-mode-syntax-table
(let ((table (make-syntax-table)))
(smie-rule-parent octave-block-offset)
;; For (invalid) code between switch and case.
;; (if (smie-parent-p "switch") 4)
- 0))))
+ nil))))
(defun octave-indent-comment ()
"A function for `smie-indent-functions' (which see)."
(back-to-indentation)
(cond
((octave-in-string-or-comment-p) nil)
- ((looking-at-p "\\s<\\{3,\\}")
+ ((looking-at-p "\\(\\s<\\)\\1\\{2,\\}")
0)
;; Exclude %{, %} and %!.
((and (looking-at-p "\\s<\\(?:[^{}!]\\|$\\)")
- (not (looking-at-p "\\s<\\s<")))
+ (not (looking-at-p "\\(\\s<\\)\\1")))
(comment-choose-indent)))))
\f
(setq-local paragraph-separate paragraph-start)
(setq-local paragraph-ignore-fill-prefix t)
(setq-local fill-paragraph-function 'octave-fill-paragraph)
- ;; FIXME: Why disable it?
- ;; (setq-local adaptive-fill-regexp nil)
- ;; Again, this is not a property of the language, don't set it here.
- ;; (setq fill-column 72)
- (setq-local normal-auto-fill-function 'octave-auto-fill)
+
+ (setq-local fill-nobreak-predicate
+ (lambda () (eq (octave-in-string-p) ?')))
+ (add-function :around (local 'comment-line-break-function)
+ #'octave--indent-new-comment-line)
(setq font-lock-defaults '(octave-font-lock-keywords))
startup file, `~/.emacs-octave'."
(interactive "P")
(let ((buffer (get-buffer-create inferior-octave-buffer)))
+ (unless arg
+ (pop-to-buffer buffer))
(unless (comint-check-proc buffer)
(with-current-buffer buffer
(inferior-octave-startup)
(inferior-octave-mode)))
- (unless arg
- (pop-to-buffer buffer))
buffer))
;;;###autoload
inferior-octave-buffer
inferior-octave-program
(append (list "-i" "--no-line-editing")
+ ;; --no-gui is introduced in Octave > 3.7
+ (when (zerop (process-file inferior-octave-program
+ nil nil nil
+ "--no-gui" "--help"))
+ (list "--no-gui"))
inferior-octave-startup-args))))
(set-process-filter proc 'inferior-octave-output-digest)
(setq inferior-octave-process proc
;; output may be mixed up). Hence, we need to digest the Octave
;; output to see when it issues a prompt.
(while inferior-octave-receive-in-progress
+ (or (process-live-p inferior-octave-process)
+ (error "Process `%s' died" inferior-octave-process))
(accept-process-output inferior-octave-process))
(goto-char (point-max))
(set-marker (process-mark proc) (point))
(when (and inferior-octave-startup-file
(file-exists-p inferior-octave-startup-file))
(format "source (\"%s\");\n" inferior-octave-startup-file))))
- (insert-before-markers
- (concat
- (if inferior-octave-output-list
- (concat (mapconcat
- 'identity inferior-octave-output-list "\n")
- "\n"))
- inferior-octave-output-string))
+ (when inferior-octave-output-list
+ (insert-before-markers
+ (mapconcat 'identity inferior-octave-output-list "\n")))
;; And finally, everything is back to normal.
(set-process-filter proc 'comint-output-filter)
;; Just in case, to be sure a cd in the startup file
;; won't have detrimental effects.
(inferior-octave-resync-dirs)
- ;; A trick to get the prompt highlighted.
+ ;; Generate a proper prompt, which is critical to
+ ;; `comint-history-isearch-backward-regexp'. Bug#14433.
(comint-send-string proc "\n")))
(defvar inferior-octave-completion-table
(setq list (cdr list)))
(set-process-filter proc filter))))
+(defvar inferior-octave-directory-tracker-resync nil)
+(make-variable-buffer-local 'inferior-octave-directory-tracker-resync)
+
(defun inferior-octave-directory-tracker (string)
"Tracks `cd' commands issued to the inferior Octave process.
Use \\[inferior-octave-resync-dirs] to resync if Emacs gets confused."
+ (when inferior-octave-directory-tracker-resync
+ (setq inferior-octave-directory-tracker-resync nil)
+ (inferior-octave-resync-dirs))
(cond
((string-match "^[ \t]*cd[ \t;]*$" string)
(cd "~"))
((string-match "^[ \t]*cd[ \t]+\\([^ \t\n;]*\\)[ \t\n;]*" string)
- (with-demoted-errors ; in case directory doesn't exist
- (cd (substring string (match-beginning 1) (match-end 1)))))))
+ (condition-case err
+ (cd (match-string 1 string))
+ (error (setq inferior-octave-directory-tracker-resync t)
+ (message "%s: `%s'"
+ (error-message-string err)
+ (match-string 1 string)))))))
(defun inferior-octave-resync-dirs ()
"Resync the buffer's idea of the current directory.
(or done (goto-char (point-min)))))))
(pcase (file-name-extension (buffer-file-name))
(`"cc" (funcall search
- "\\_<DEFUN\\s-*(\\s-*\\(\\(?:\\sw\\|\\s_\\)+\\)" 1))
+ "\\_<DEFUN\\(?:_DLD\\)?\\s-*(\\s-*\\(\\(?:\\sw\\|\\s_\\)+\\)" 1))
(t (funcall search octave-function-header-regexp 3)))))
(defun octave-function-file-p ()
\f
;;; Indentation
-(defun octave-indent-new-comment-line ()
+(defun octave-indent-new-comment-line (&optional soft)
+ ;; FIXME: C-M-j should probably be bound globally to a function like
+ ;; this one.
"Break Octave line at point, continuing comment if within one.
-If within code, insert `octave-continuation-string' before breaking the
-line. If within a string, signal an error.
-The new line is properly indented."
+Insert `octave-continuation-string' before breaking the line
+unless inside a list. Signal an error if within a single-quoted
+string."
(interactive)
- (delete-horizontal-space)
+ (funcall comment-line-break-function soft))
+
+(defun octave--indent-new-comment-line (orig &rest args)
(cond
- ((octave-in-comment-p)
- (indent-new-comment-line))
- ((octave-in-string-p)
- (error "Cannot split a code line inside a string"))
+ ((octave-in-comment-p) nil)
+ ((eq (octave-in-string-p) ?')
+ (error "Cannot split a single-quoted string"))
+ ((eq (octave-in-string-p) ?\")
+ (insert octave-continuation-string))
(t
- (insert (concat " " octave-continuation-string))
- (reindent-then-newline-and-indent))))
+ (delete-horizontal-space)
+ (unless (and (cadr (syntax-ppss))
+ (eq (char-after (cadr (syntax-ppss))) ?\())
+ (insert " " octave-continuation-string))))
+ (apply orig args)
+ (indent-according-to-mode))
(define-obsolete-function-alias
'octave-indent-defun 'prog-indent-sexp "24.4")
(when (and (> arg 0) (/= orig (point)))
(setq arg (1- arg)))
(forward-sexp (- arg))
+ (and (< arg 0) (forward-sexp -1))
(/= orig (point))))
-\f
-;;; Filling
-(defun octave-auto-fill ()
- "Perform auto-fill in Octave mode.
-Returns nil if no feasible place to break the line could be found, and t
-otherwise."
- (let (fc give-up)
- (if (or (null (setq fc (current-fill-column)))
- (save-excursion
- (beginning-of-line)
- (and auto-fill-inhibit-regexp
- (octave-looking-at-kw auto-fill-inhibit-regexp))))
- nil ; Can't do anything
- (if (and (not (octave-in-comment-p))
- (> (current-column) fc))
- (setq fc (- fc (+ (length octave-continuation-string) 1))))
- (while (and (not give-up) (> (current-column) fc))
- (let* ((opoint (point))
- (fpoint
- (save-excursion
- (move-to-column (+ fc 1))
- (skip-chars-backward "^ \t\n")
- ;; If we're at the beginning of the line, break after
- ;; the first word
- (if (bolp)
- (re-search-forward "[ \t]" opoint t))
- ;; If we're in a comment line, don't break after the
- ;; comment chars
- (if (save-excursion
- (skip-syntax-backward " <")
- (bolp))
- (re-search-forward "[ \t]" (line-end-position)
- 'move))
- ;; If we're not in a comment line and just ahead the
- ;; continuation string, don't break here.
- (if (and (not (octave-in-comment-p))
- (looking-at
- (concat "\\s-*"
- (regexp-quote
- octave-continuation-string)
- "\\s-*$")))
- (end-of-line))
- (skip-chars-backward " \t")
- (point))))
- (if (save-excursion
- (goto-char fpoint)
- (not (or (bolp) (eolp))))
- (let ((prev-column (current-column)))
- (if (save-excursion
- (skip-chars-backward " \t")
- (= (point) fpoint))
- (progn
- (octave-maybe-insert-continuation-string)
- (indent-new-comment-line t))
- (save-excursion
- (goto-char fpoint)
- (octave-maybe-insert-continuation-string)
- (indent-new-comment-line t)))
- (if (>= (current-column) prev-column)
- (setq give-up t)))
- (setq give-up t))))
- (not give-up))))
-
(defun octave-fill-paragraph (&optional _arg)
"Fill paragraph of Octave code, handling Octave comments."
;; FIXME: difference with generic fill-paragraph:
(and (= (current-column) cfc) (eolp)))
(forward-line 1)
(if (not (eolp)) (insert " "))
- (or (octave-auto-fill)
+ (or (funcall normal-auto-fill-function)
(forward-line 1))))
t)))
-\f
;;; Completions
(defun octave-completion-at-point ()
(octave-help
(buffer-substring (button-start b) (button-end b)))))
-(defvar help-xref-following)
+(defvar octave-help-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\M-." 'octave-find-definition)
+ (define-key map "\C-hd" 'octave-help)
+ map))
+
+(define-derived-mode octave-help-mode help-mode "OctHelp"
+ "Major mode for displaying Octave documentation."
+ :abbrev-table nil
+ :syntax-table octave-mode-syntax-table
+ (eval-and-compile (require 'help-mode))
+ ;; Mostly stolen from `help-make-xrefs'.
+ (let ((inhibit-read-only t))
+ (setq-local info-lookup-mode 'octave-mode)
+ ;; Delete extraneous newlines at the end of the docstring
+ (goto-char (point-max))
+ (while (and (not (bobp)) (bolp))
+ (delete-char -1))
+ (insert "\n")
+ (when (or help-xref-stack help-xref-forward-stack)
+ (insert "\n"))
+ (when help-xref-stack
+ (help-insert-xref-button help-back-label 'help-back
+ (current-buffer)))
+ (when help-xref-forward-stack
+ (when help-xref-stack
+ (insert "\t"))
+ (help-insert-xref-button help-forward-label 'help-forward
+ (current-buffer)))
+ (when (or help-xref-stack help-xref-forward-stack)
+ (insert "\n"))))
+
+(defvar octave-help-mode-finish-hook nil
+ "Octave specific hook for `temp-buffer-show-hook'.")
+
+(defun octave-help-mode-finish ()
+ (when (eq major-mode 'octave-help-mode)
+ (run-hooks 'octave-help-mode-finish-hook)))
+
+(add-hook 'temp-buffer-show-hook 'octave-help-mode-finish)
(defun octave-help (fn)
"Display the documentation of FN."
(interactive (list (octave-completing-read)))
(inferior-octave-send-list-and-digest
(list (format "help \"%s\"\n" fn)))
- (let ((lines inferior-octave-output-list))
+ (let ((lines inferior-octave-output-list)
+ (inhibit-read-only t))
(when (string-match "error: \\(.*\\)$" (car lines))
(error "%s" (match-string 1 (car lines))))
(with-help-window octave-help-buffer
(let ((help-xref-following t))
(help-setup-xref (list 'octave-help fn)
(called-interactively-p 'interactive)))
- (setq-local info-lookup-mode 'octave-mode)
;; Note: can be turned off by suppress_verbose_help_message.
;;
;; Remove boring trailing text: Additional help for built-in functions
(when (re-search-forward "from the file \\(.*\\)$"
(line-end-position)
t)
- (let ((file (match-string 1)))
+ (let* ((file (match-string 1))
+ (dir (file-name-directory
+ (directory-file-name (file-name-directory file)))))
(replace-match "" nil nil nil 1)
(insert "`")
- (help-insert-xref-button (file-name-nondirectory file)
+ ;; Include the parent directory which may be regarded as
+ ;; the category for the FN.
+ (help-insert-xref-button (file-relative-name file dir)
'octave-help-file fn)
(insert "'")))
;; Make 'See also' clickable
(with-syntax-table octave-mode-syntax-table
(when (re-search-forward "^\\s-*See also:" nil t)
- (while (re-search-forward "\\_<\\(?:\\sw\\|\\s_\\)+\\_>" nil t)
- (make-text-button (match-beginning 0)
- (match-end 0)
- :type 'octave-help-function))))))))
+ (let ((end (save-excursion (re-search-forward "^\\s-*$" nil t))))
+ (while (re-search-forward "\\_<\\(?:\\sw\\|\\s_\\)+\\_>" end t)
+ (make-text-button (match-beginning 0)
+ ;; If the match ends with . exclude it.
+ (if (eq (char-before (match-end 0)) ?.)
+ (1- (match-end 0))
+ (match-end 0))
+ :type 'octave-help-function)))))
+ (octave-help-mode)))))
(defcustom octave-source-directories nil
"A list of directories for Octave sources.