matlab.el:

Support some block keywords like 'arguments' to be used as a variable or fnc name.
(matlab-keyword-list): Remove MCOS keywords.
(matlab-keyword-first-on-line-list): New, is MCOS keywords from a bove.
(matlab-font-lock-keywords): Show first-on-line keywords only if first on a line.
(matlab-mcos-innerblock-regexp): New
(matlab-mcos-regexp): Now uses above, but add classdef.
(matlab-block-syntax-re, matlab-innerblock-syntax-re): New
(matlab-block-start-scan-re): New
(matlab-backward-sexp): When block start found, use `-on-valid-block-start' in addition to
'-in-string-or-comment'.
(matlab-forward-sexp): Same check when block start is found.
Also new input 'parentblock' can be used to speed up valid block start check.
Use the new input when recursing.
Use 'matlab-block-start-scan-re' instead of building local regexp.
(matlab-valid-block-start-slow-and-careful): New setting
(matlab-cursor-on-valid-block-start): New.
(matlab-current-syntactic-block): New
(matlab-lattr-block-cont): Make sure blocks found are valid.
(matlab-show-paren-or-block): Make sure blocks found are valid.
Use new block-re fcns for regexp.
This commit is contained in:
Eric Ludlam 2021-02-25 21:10:20 -05:00
parent 00e25f897c
commit c12e9bcc65

191
matlab.el
View file

@ -1065,15 +1065,20 @@ Argument LIMIT is the maximum distance to search."
"return" "break" "continue"
"switch" "case" "otherwise" "try"
"catch" "tic" "toc"
;; MCOS keywords
"properties" "methods" "enumeration" "events"
"arguments"
)
"List of keywords for MATLAB used in highlighting.
Customizing this variable is only useful if `regexp-opt' is available."
:group 'matlab
:type '(repeat (string :tag "Keyword: ")))
(defcustom matlab-keyword-first-on-line-list '( "properties" "methods" "enumeration" "events"
"arguments"
)
"List of keywords for MATLAB that should be highilghted only if the first word on a line.
Customizing this variable is only useful if `regexp-opt' is available."
:group 'matlab
:type '(repeat (string :tag "Keyword: ")))
(defcustom matlab-handle-graphics-list '("figure" "axes" "axis" "line"
"surface" "patch" "text" "light"
"image" "set" "get" "uicontrol"
@ -1136,6 +1141,11 @@ Uses `regex-opt' if available. Otherwise creates a 'dumb' expression."
;; General keywords
(list (matlab-font-lock-regexp-opt matlab-keyword-list)
'(0 font-lock-keyword-face))
;; Keywords that should be the first word on a line
(list (concat "^\\s-*\\("
(matlab-font-lock-regexp-opt matlab-keyword-first-on-line-list)
"\\)")
'(1 font-lock-keyword-face))
;; The end keyword is only a keyword when not used as an array
;; dereferencing part.
'("\\(^\\|[;,]\\)[ \t]*\\(end\\)\\b"
@ -1739,7 +1749,12 @@ Return nil if it is being used to dereference an array."
(defconst matlab-defun-regex "^\\(\\s-*function\\|classdef\\)[ \t.[]"
"Regular expression defining the beginning of a MATLAB function.")
(defconst matlab-mcos-regexp "\\|classdef\\|properties\\|methods\\|events\\|enumeration\\|arguments"
(defconst matlab-mcos-innerblock-regexp "\\|properties\\|methods\\|events\\|enumeration\\|arguments"
"Keywords which mark the beginning of mcos blocks.
These keywords can be overriden as variables or functions in other contexts
asside from that which they declare their content.")
(defconst matlab-mcos-regexp (concat "\\|classdef" matlab-mcos-innerblock-regexp)
"Keywords which mark the beginning of mcos blocks.")
(defcustom matlab-block-indent-tic-toc-flag nil
@ -1748,6 +1763,16 @@ This variable should be set before loading matlab.el"
:group 'matlab
:type 'boolean)
(defconst matlab-block-syntax-re
(concat "\\(function" matlab-mcos-regexp "\\)\\>")
"Keywords that represent blocks that have custom internal syntax.
Used by `matlab-cursor-on-valid-block-start'.")
(defconst matlab-innerblock-syntax-re
(concat "\\(function" matlab-mcos-innerblock-regexp "\\)\\>")
"Keywords that represent blocks that have custom internal syntax.
Used by `matlab-cursor-on-valid-block-start'.")
(defconst matlab-block-beg-pre-if
(if matlab-block-indent-tic-toc-flag
(concat "function\\|parfor\\|spmd\\|for\\|while\\|if\\|switch\\|try\\|tic"
@ -1814,6 +1839,13 @@ blocks.")
(matlab-block-end-pre) "\\|"
matlab-endless-blocks "\\)\\b"))
(defun matlab-block-start-scan-re ()
"Expression used to scan over matching pairs of begin/ends.
Assume cursor is on the beginning of the upcoming word."
(concat "\\("
(matlab-block-beg-pre) "\\|"
matlab-block-mid-pre "\\)\\b"))
(defun matlab-block-scan-re ()
"Expression used to scan over matching pairs of begin/ends."
(concat "\\(^\\|[;,]\\)\\s-*\\("
@ -2062,7 +2094,10 @@ This assumes that expressions do not cross \"function\" at the left margin."
(unless (matlab-backward-sexp nil noerror)
(setq done t
returnme nil)))
(if (not (matlab-cursor-in-string-or-comment))
;; Make sure we are at a valid block construct and not
;; comments or other weird spot.
(if (and (not (matlab-cursor-in-string-or-comment))
(matlab-cursor-on-valid-block-start))
(setq done t))))
(goto-char start)
(if noerror
@ -2071,11 +2106,13 @@ This assumes that expressions do not cross \"function\" at the left margin."
(error "Unstarted END construct"))))
returnme)))))
(defun matlab-forward-sexp (&optional includeelse autostart)
(defun matlab-forward-sexp (&optional includeelse autostart parentblock)
"Go forward one balanced set of MATLAB expressions.
Optional argument INCLUDEELSE will stop on ELSE if it matches the starting IF.
If AUTOSTART is non-nil, assume we are already inside a block, and navigate
forward until we exit that block."
forward until we exit that block.
PARENTBLOCK is used when recursing to validate block starts as being in
a valid context."
(interactive "P")
(let (p) ;; go to here if no error.
(save-excursion ;; don't go anywhere if there is an error
@ -2092,11 +2129,8 @@ forward until we exit that block."
)
;; No autostart, and looking at a block keyword.
((and (not autostart)
(or (not (looking-at (concat "\\("
(matlab-block-beg-pre)
"\\|"
(matlab-block-mid-re)
"\\)\\>")))
(or (not (looking-at (matlab-block-start-scan-re)))
;; ^^ (concat "\\(" (matlab-block-beg-pre) "\\|" (matlab-block-mid-re) "\\)\\>")
(matlab-cursor-in-string-or-comment)))
;; Go forwards one simple expression
(matlab-move-simple-sexp-internal 1))
@ -2107,8 +2141,11 @@ forward until we exit that block."
;; Default behavior.
(t
;; Not autostart, skip next word.
(unless autostart (forward-word 1))
;; Not autostart, skip next word, and also track what it is
;; for nesting purposes.
(unless autostart
(setq parentblock (buffer-substring-no-properties
(point) (progn (forward-word 1) (point)))))
(let ((done nil) (s nil)
(expr-scan (if includeelse
(matlab-block-re)
@ -2120,11 +2157,18 @@ forward until we exit that block."
(pos-visible-in-window-p)))
(goto-char (match-beginning 2))
(if (looking-at expr-look)
(if (matlab-cursor-in-string-or-comment)
(if (or (matlab-cursor-in-string-or-comment)
;; We'd like to do this:
;;(not (matlab-cursor-on-valid-block-start))
;; but it travels backwards. Instead, we need to track the
;; last block we are diving down from and just use that
;; instead of looking it up along the way.
(not (matlab-cursor-on-valid-block-start parentblock))
)
(forward-word 1)
;; we must skip the expression and keep searching
;; NEVER EVER call with value of INCLUDEELSE
(matlab-forward-sexp))
(matlab-forward-sexp nil nil (match-string-no-properties 1)))
(forward-word 1)
(if (and (not (matlab-cursor-in-string-or-comment))
(matlab-valid-end-construct-p))
@ -2134,6 +2178,106 @@ forward until we exit that block."
(setq p (point)))) ;; really go here
(goto-char p)))
(defvar matlab-valid-block-start-slow-and-careful t
"Be very careful with determining of a block is valid when t.
Set to nil if fast-and-loose is ok.")
(defun matlab-cursor-on-valid-block-start (&optional known-parent-block)
"Return t if cursor is on a valid block start.
Valid block starts are those that represent a syntax context, like function,
classdef, properties, etc.
KNOWN-PARENT-BLOCK is a string that represents the context cursor is in.
Use this if you know what context you're in."
(save-match-data
(save-restriction
(widen)
(cond
((not (looking-at (matlab-block-beg-re)))
;; Not looking at a valid block
nil)
;; Else, are we on a block that has special syntax?
((not (looking-at matlab-innerblock-syntax-re))
;; Not an innerblock syntax that only work withing special blocks
;; thus automatically true.
t)
;; Else, a special block. We need to check the context of this
;; block to know if this innerblock is valid.
;; Cheap check - if functions don't have end, then always invalid
;; since these context blocks can't exist.
((not matlab-functions-have-end)
t)
;; Cheap check - is this block keyword not the first word on the line?
((save-excursion (skip-syntax-backward " ") (not (bolp)))
;; Not first on line, not valid block.
;; Technically it COULD be valid, but we need some cheap ways
;; to skip over some types of syntaxes that look dumb.
nil)
;; Expensive check - is this block in the right context?
((or matlab-valid-block-start-slow-and-careful known-parent-block)
(let ((foundblock (match-string-no-properties 1))
(myblock (if known-parent-block
(cons known-parent-block nil) ;; shortcut if known
(matlab-current-syntactic-block))))
(cond ((and (string= foundblock "arguments")
(string= (car myblock) "function"))
;; We found correct usage of arguments.
t)
((and (string= foundblock "function")
(or (not (car myblock))
(string= (car myblock) "methods")))
;; We found correct usage of function.
t)
((string= (car myblock) "classdef")
;; We found correct usage of methods, events, etc.
t)
(t
;; Some other case is bad.
nil)))
)
;; A cheap version of the expensive check
((and (not matlab-valid-block-start-slow-and-careful)
(looking-at matlab-innerblock-syntax-re))
;; If we are not slow and careful, we just need to return t if we see
;; of of these keywords since these were filtered out earlier.
t)
;; If none of the valid cases, must be invalid
(t nil)
))))
(defun matlab-current-syntactic-block ()
"Return information about the current syntactic block the cursor is in.
Value returned is of the form ( BLOCK-TYPE . PT ) where BLOCK-TYPE is a
string, such as 'function' or 'properties', and PT is the location that
the block starts at.
This function skips over blocks such as 'switch' and 'if', and only returns
blocks that change the syntax of their contents, such as:
function, classdef, properties, events, methods, arguments
"
(let ((block-type nil)
(block-beg nil))
(save-excursion
;; By specifying NOERROR, returns nil if we can't move
;; backward, which means we should stop.
;; backward sexp also knows to skip invlaid block starts, so we'll
;; only land on safe blocks.
(when (and (matlab-backward-sexp t t)
(looking-at matlab-block-syntax-re))
(setq block-type (match-string-no-properties 1)
block-beg (point)))
)
(cons block-type block-beg)))
(defun matlab-indent-sexp ()
"Indent the syntactic block starting at point."
(interactive)
@ -2506,8 +2650,11 @@ Optional EOL indicates a virtual end of line."
(matlab-navigation-syntax
(while (re-search-forward (concat "\\<" (matlab-block-beg-re) "\\>")
nil t)
(if (matlab-cursor-in-string-or-comment)
;; Do nothing
(if (or (matlab-cursor-in-string-or-comment)
(not (save-excursion (forward-word -1)
(matlab-cursor-on-valid-block-start))))
;; Do nothing if in comment, or if the thing we skipped over was
;; an invalid block construct (based on local context)
nil
;; Increment counter, move to end.
(setq v (1+ v))
@ -3876,7 +4023,7 @@ Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)"
(t
;; Part 2: Are we looking at a block start/end, such as if end;
;; If we are on On a word character, or just after a
;; If we are on a word character, or just after a
;; word character move back one symbol. This will let
;; us use the block begin / end matchers to figure
;; out where we are.
@ -3899,7 +4046,11 @@ Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)"
there-end (match-end 0)
mismatch nil)
)
((looking-at (concat (matlab-block-beg-re) "\\>"))
((matlab-cursor-on-valid-block-start)
;; Above is similar to vvv but with special checks.
;; Still need to do vvv b/c we need the match-data.
(looking-at (concat (matlab-block-beg-re) "\\>"))
;; We are at the beginning of a block. Navigate forward to the end
;; statement.
(setq here-beg (match-beginning 0)