matlab.el:

(matlab-mode-for-new-mfiles): New setting.
(matlab-is-matlab-file): New test for magic-mode-alist.
(matlab-is-mcr-file): New test for MCR files.
(auto-mode-alist -> magic-mode-alist): Use above to
decide if a file is an objective C file.  If not, do MATLAB.
(matlab-mode-leave): When matlab-mode exists, disable mlint mode.
(matlab-mode): Add change-major-mode-hook for `matlab-mode-leave'
(matlab-frame-init, matlab-ltype-block-comm)
(matlab-electric-block-comment, matlab-toggle-read-only)
Spelling fixes.
This commit is contained in:
Eric Ludlam 2020-08-15 21:02:30 -04:00
parent 6bcee3f537
commit 3402cdebf4
1 changed files with 129 additions and 50 deletions

179
matlab.el
View File

@ -64,6 +64,22 @@
:prefix "matlab-"
:group 'languages)
(defcustom matlab-mode-for-new-mfiles 'maybe
"*Enter `matlab-mode' for new *.m files.
The `matlab' package will automatically enter `matlab-mode' when
the first part of a *.m file is doesn't contain Objective-C
comments or '#' characters. If you want new (empty) files to
automatically enter `matlab-mode', specify this item as
t (always). If you specify 'maybe, new files will enter
`matlab-mode' when you have an existing MATLAB buffer. Specifying
nil (never) means that new *.m files will not enter
`matlab-mode', and with default Emacs settings they will enter
`objc-mode'"
:group 'matlab
:type '(choice (const :tag "Always" t)
(const :tag "Never" nil)
(const :tag "Maybe" maybe)))
(defcustom matlab-indent-level 4
"*The basic indentation amount in `matlab-mode'."
:group 'matlab
@ -659,7 +675,7 @@ If font lock is not loaded, lay in wait."
:help "When MATLAB debugger is active, set break point at current M-file point"]
["Remove Breakpoint (ebclear in FILE at point)" gud-remove
:active (matlab-shell-active-p)
:help "Show all active breakpoints in a seperate buffer." ]
:help "Show all active breakpoints in a separate buffer." ]
["List Breakpoints (ebstatus)" gud-list-breakpoints
:active (matlab-shell-active-p)
:help "List active breakpoints."]
@ -780,7 +796,7 @@ when attempting to understand the current context.")
(goto-char (match-beginning 0))
(call-interactively 'set-mark-command)
(goto-char (match-end 0)))
(defun matlab-font-lock-allstring-comment-match-normal (limit)
"When font locking strings, call this function for normal character vectors.
Argument LIMIT is the maximum distance to scan."
@ -810,7 +826,7 @@ Argument LIMIT is the maximum distance to scan."
)
;; Not a comment, must be a string or charvec
;; Scan to end of string by looking at every matching
;; string character and deciding what it means.
(while (and (not done)
@ -827,7 +843,7 @@ Argument LIMIT is the maximum distance to scan."
done t))
;; The other type of string - just keep going.
nil))
;; If not done, unterminated
(if (not done)
(setq bu b0
@ -845,7 +861,7 @@ Argument LIMIT is the maximum distance to scan."
;; Move to the end
(goto-char (or es eu ec))
;; Successful string
t)))
@ -1222,11 +1238,11 @@ Uses `regex-opt' if available. Otherwise creates a 'dumb' expression."
(list (concat "^\\s-*\\(classdef\\)"
matlab-class-attributes-list-re
"\\s-+\\(\\sw+\\)")
'("\\s-*[<&]\\s-*\\(\\(\\sw\\|\\.\\)+\\)" nil nil
'("\\s-*[<&]\\s-*\\(\\(\\sw\\|\\.\\)+\\)" nil nil
(1 font-lock-constant-face)))
;; Property and Method blocks have attributes to highlight
(list "^\\s-*\\(classdef\\|properties\\|methods\\|events\\|arguments\\)\\s-*("
'("\\(\\sw+\\)\\s-*\\(=\\s-*[^,)]+\\)?" nil nil
'("\\(\\sw+\\)\\s-*\\(=\\s-*[^,)]+\\)?" nil nil
(1 font-lock-type-face)
))
;; Properties can have a type syntax after them
@ -1289,14 +1305,68 @@ Uses `regex-opt' if available. Otherwise creates a 'dumb' expression."
;; Imenu support.
(defvar matlab-imenu-generic-expression
'((nil "^\\s-*function\\>[ \t\n.]*\\(\\(\\[[^]]*\\]\\|\\sw+\\)[ \t\n.]*\
< =\[ \t\n.]*\\)?\\([a-zA-Z0-9_]+\\)" 3))
< =[ \t\n.]*\\)?\\([a-zA-Z0-9_]+\\)" 3))
"Expressions which find function headings in MATLAB M files.")
;;; MATLAB mode entry point ==================================================
;; Choose matlab-mode if when loading MATLAB *.m files
;; See "How Emacs Chooses a Major Mode"
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Auto-Major-Mode.html
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.m$" . matlab-mode))
(defun matlab-is-matlab-file ()
"Enter `matlab-mode' when file content looks like a MATLAB *.m
file or for empty files *.m files when `matlab-mode-for-new-mfiles'
indicates as such."
(and buffer-file-name ;; have a file?
;; AND a valid MATLAB file name
(string-match "^\\(.*/\\)?[a-zA-Z][a-zA-Z0-9_]*\\.m$" buffer-file-name)
;; AND (have MATLAB code OR an empty file that should enter matlab-mode)
(or
;; Is content MATLAB code? We can definitely identify *some* MATLAB content using
;; (looking-at "^[[:space:]\n]*\\(%\\|function\\|classdef\\)")
;; i.e. '%', '%{' comments, or function/classdef start, but this fails to find MATLAB
;; scripts. Thus, if buffer is NOT Objective-C and has something in it, we assume MATLAB.
;; Objective-c is identified by
;; - comment start chars: // or /*,
;; - # char (as in #import)
;; - @ char (as in @interface)
;; MATLAB scripts are identified by the start of a valid identifier, i.e. a letter or
;; some math operation, e.g. [1,2,3]*[1,2,3]', thus all we really need to look for
;; is a non-whitespace character which could be a MATLAB comment, generic MATLAB commands,
;; function/classdef, etc.
(and (not (looking-at "^[[:space:]\n]*\\(//\\|/\\*\\|#\\|@\\)"))
(looking-at "^[[:space:]\n]*[^[:space:]\n]"))
;; Empty file - enter matlab-mode based on `matlab-mode-for-new-mfiles' setting
(and (= (buffer-size) 0)
(or (equal matlab-mode-for-new-mfiles t)
(and (equal matlab-mode-for-new-mfiles 'maybe)
;; Enter matlab-mode if we already have a buffer in matlab-mode
(let ((buffers (buffer-list))
enter-matlab-mode)
(while buffers
(with-current-buffer (car buffers)
(when (or (eq major-mode 'matlab-mode)
(eq major-mode 'matlab-shell-mode))
(setq enter-matlab-mode t)
(setq buffers nil)))
(setq buffers (cdr buffers)))
enter-matlab-mode)))))))
;;;###autoload
(add-to-list 'magic-mode-alist '(matlab-is-matlab-file . matlab-mode))
;; MCR (MATLAB common run-time) *.m should enter fundamental-mode because they are binary.
;;;###autoload
(defun matlab-is-mcr-file ()
(and buffer-file-name
(string-match "^\\(.*/\\)?[a-zA-Z][a-zA-Z0-9_]*\\.m$" buffer-file-name)
(looking-at "^[A-Z0-9]+MCR")))
;;;###autoload
(add-to-list 'magic-mode-alist '(matlab-is-mcr-file . fundamental-mode))
(defvar mlint-minor-mode)
(declare-function mlint-minor-mode "mlint.el")
@ -1305,6 +1375,11 @@ Uses `regex-opt' if available. Otherwise creates a 'dumb' expression."
(declare-function mlint-clear-cross-function-variable-highlighting "mlint.el")
(defvar show-paren-data-function)
(defun matlab-mode-leave ()
"When leaving `matlab-mode', turn off `mlint-minor-mode'"
(when (eq major-mode 'matlab-mode)
(mlint-minor-mode -1)))
;;;###autoload
(define-derived-mode matlab-mode prog-mode "MATLAB"
"MATLAB(R) mode is a major mode for editing MATLAB dot-m files.
@ -1361,7 +1436,7 @@ Variables:
All Key Bindings:
\\{matlab-mode-map}"
:after-hook (matlab-mode-init-mlint-if-needed)
(kill-all-local-variables)
(use-local-map matlab-mode-map)
(setq major-mode 'matlab-mode)
@ -1434,13 +1509,13 @@ All Key Bindings:
)
;; Enable our own block highlighting if paren mode not around.
(matlab-enable-block-highlighting 1))
(if window-system (matlab-frame-init))
;; built-in sexp navigation
(make-local-variable 'forward-sexp-function)
(setq forward-sexp-function #'matlab-move-simple-sexp-internal)
;; If first function is terminated with an end statement, then functions have
;; ends.
(if (matlab-do-functions-have-end-p)
@ -1491,6 +1566,9 @@ All Key Bindings:
(t)
)
;; When leaving matlab-mode, turn off mlint
(add-hook 'change-major-mode-hook #'matlab-mode-leave)
(if matlab-vers-on-startup (matlab-show-version)))
(defun matlab-mode-init-mlint-if-needed ()
@ -1511,7 +1589,7 @@ All Key Bindings:
;; If there is an error loading the stuff, don't
;; continue.
(error nil))))
(error nil))))
;;; Utilities =================================================================
@ -1861,7 +1939,7 @@ If COUNT is negative, travel backward."
(if (> 0 count)
(skip-chars-backward " \t;.")
(skip-chars-forward " \t;."))
(let* ((bounds nil)
(ctxt (matlab-cursor-comment-string-context 'bounds))
(skipnav nil))
@ -1885,7 +1963,7 @@ If COUNT is negative, travel backward."
(forward-char (if (> 0 count) -1 1)))
(setq skipnav t)
)))
(unless skipnav
;; Outside of comments and strings, look at our local syntax and decide what to do.
;; Skip over whitespace to see what the next interesting thing is.
@ -1893,12 +1971,12 @@ If COUNT is negative, travel backward."
(if (< 0 count)
(progn ;; forward motion
(skip-chars-forward " \t\n;.=")
(cond ((or (looking-at "['\"]\\|%\\|\\.\\.\\."))
;; In a comment or string.
(forward-char 1)
(matlab-up-string-or-comment))
((looking-at "\\s(")
;; At the beginning of a list, matrix, cell, whatever.
(matlab-move-list-sexp-internal count))
@ -1911,12 +1989,12 @@ If COUNT is negative, travel backward."
;; backward motion
(skip-chars-backward " \t\n;.=")
(let ((ctxt2 (matlab-cursor-comment-string-context)))
(cond ((or (looking-back "['\"]" (- (point) 2))
(and (eolp) (or (eq ctxt2 'comment) (eq ctxt2 'elipsis))))
(backward-char 2)
(matlab-backward-up-string-or-comment))
((looking-back "\\s)" (- (point) 1))
;; At the end of a list, matrix, cell, etc
(matlab-move-list-sexp-internal count))
@ -1960,7 +2038,7 @@ This assumes that expressions do not cross \"function\" at the left margin."
(matlab-cursor-in-string-or-comment))))
;; Go backwards one simple expression
(matlab-move-simple-sexp-internal -1))
;; otherwise go backwards recursively across balanced expressions
;; backup over our end
(t
@ -2243,7 +2321,7 @@ of character based."
(lcbounds matlab-ltype-block-comm-lastcompute))
;;(if bounds (message "Recycle bounds") (message "no recycle bounds"))
(cond ((and bounds (>= (point) (car bounds))
(<= (point) (cdr bounds)))
;; All set!
@ -2253,7 +2331,7 @@ of character based."
(<= (point) (cdr lcbounds)))
;; Also all set
nil)
(t
(setq bounds (matlab-ltype-block-comm-1)))
)
@ -2263,8 +2341,8 @@ of character based."
;; cache point location
(setq matlab-ltype-block-comm-lastcompute
(cons (point-at-bol) (point-at-eol)))
;; Else clear lastcompute loation
;; Else clear lastcompute location
(setq matlab-ltype-block-comm-lastcompute nil)
;; Check if caller wants line bounds.
(when (and linebounds bounds)
@ -2286,11 +2364,11 @@ of character based."
(if (and (matlab-ltype-block-comment-start)
(not (matlab-cursor-in-string-or-comment)))
(setq start (match-beginning 0)) ;; CHECK
(while (and (setq good (re-search-backward "^\\s-*\\%\\([{}]\\)\\s-*$" nil t))
(matlab-cursor-in-string-or-comment))
nil)
(when (and good (matlab-ltype-block-comment-start))
(setq start (match-beginning 0))))
@ -2298,7 +2376,7 @@ of character based."
(while (and (setq good (re-search-forward matlab-block-comment-end-re nil t))
(matlab-cursor-in-string t))
nil)
(if (and good (goto-char (match-beginning 0)) (matlab-ltype-block-comment-end))
(setq end (match-end 0))
(setq end (point-max))))
@ -2567,7 +2645,7 @@ line."
(when (featurep 'pulse)
(pulse-momentary-highlight-region (car bounds) (car (cdr bounds)))
))))
(defun matlab-cursor-comment-string-context (&optional bounds-sym)
"Return the comment/string context of cursor for the current line.
@ -2631,7 +2709,7 @@ bounds of the string or comment the cursor is in"
(if (or (eq returnme 'comment) (eq returnme 'elipsis))
;; Comments and elipsis always end at end of line.
(set bounds-sym (list laststart (point-at-eol)))
;; Strings/charvec we need to keep searching forward.
(save-excursion
(let ((done nil)
@ -2656,7 +2734,7 @@ bounds of the string or comment the cursor is in"
(when (not done)
(set bounds-sym (list laststart (point-at-eol))))
))))
;; Return the identified context.
returnme))))
@ -2904,7 +2982,7 @@ Argument CURRENT-INDENTATION is what the previous line recommends for indentatio
;; (beginning-of-line)
;; (back-to-indentation)
;; (= (point) (progn (matlab-beginning-of-command) (point))))
;; This means we are at the beginning of a command structure.
;; Always match up against the previous line.
(list 'code ci))
@ -3199,18 +3277,18 @@ Argument ARG specifies how many %s to insert."
(interactive "P")
(self-insert-command (or arg 1))
(let ((bc (save-excursion (beginning-of-line) (matlab-ltype-block-comm))))
(cond ((matlab-ltype-block-comment-start)
;; Starting block comment. Check if we are alreayd in a block
;; Starting block comment. Check if we are already in a block
;; comment, and blink it if a problem.
(let ((bcwrapped (save-excursion
(beginning-of-line)
(matlab-ltype-block-comm))))
;; Regardless, indent our linel
;; Regardless, indent our line
(matlab-indent-line)
(when bcwrapped
(save-excursion
(goto-char (car bcwrapped))
@ -3651,6 +3729,7 @@ ARG is passed to `fill-paragraph' and will justify the text."
(if (looking-at "%%")
(progn (end-of-line)
(forward-char 1)))
(beginning-of-line)
(point)))
(end (save-excursion (matlab-end-of-command)
(point)))
@ -3726,7 +3805,7 @@ INTERACTIVE is ignored."
gud-matlab-debug-active)
;; The debugging is active, just re-enable debugging read-only-mode
(matlab-shell-gud-minor-mode 1)
;; Else - it is not - probably doing somethng else.
;; Else - it is not - probably doing something else.
(call-interactively 'read-only-mode)
))
@ -3760,7 +3839,7 @@ Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)"
;; where 4 == open paren and 5 == close paren
;; and c is the char that closes the open or close paren
;; These checks are much faster than regexp
;; Step one - check for parens
(cond ((and here-syntax (= (car here-syntax) 4)) ; open paren
(setq here-beg (point)
@ -3806,7 +3885,7 @@ Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)"
(forward-symbol -1))
(matlab-navigation-syntax
(condition-case err
(cond
((looking-at "function\\>")
@ -3869,15 +3948,15 @@ Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)"
there-end (match-end 0)
mismatch nil)
)
;; No block matches, just return nothing.
(t (setq noreturn t))
)
;; An error occurred. Assume 'here-*' is set, and setup mismatch.
(error (setq mismatch t)))
)))
(if noreturn
@ -4330,12 +4409,12 @@ desired. Optional argument FAST is not used."
;;; matlab.el ends here
;; LocalWords: el Wette mwette caltech edu Ludlam eludlam defconst online
;; LocalWords: compat easymenu defcustom CASEINDENT COMMANDINDENT sexp defun
;; LocalWords: mmode setq progn sg Fns Alist elipsis vf functionname vers
;; LocalWords: minibuffer featurep fboundp facep zmacs defface cellbreak
;; LocalWords: cellbreaks overline keymap torkel ispell gud allstring strchar
;; LocalWords: bs eu bc ec searchlim eol charvec Matchers ltype cdr if'd
;; LocalWords: el Wette mwette caltech edu Ludlam eludlam defconst online mfiles ebstop ebclear
;; LocalWords: compat easymenu defcustom CASEINDENT COMMANDINDENT sexp defun ebstatus mlg gud's
;; LocalWords: mmode setq progn sg Fns Alist elipsis vf functionname vers subjob flb fle elisp
;; LocalWords: minibuffer featurep fboundp facep zmacs defface cellbreak bcend lastcompute noblock
;; LocalWords: cellbreaks overline keymap torkel ispell gud allstring strchar decl lcbounds setcar
;; LocalWords: bs eu bc ec searchlim eol charvec Matchers ltype cdr if'd setcdr bcwrapped
;; LocalWords: uicontext setcolor mld keywordlist mapconcat pragmas Classdefs
;; LocalWords: dem Za Imenu imenu alist prog reindent unindent boundp fn
;; LocalWords: Parens symbolp prev lst nlst nreverse Aki Vehtari backquote