Merge the hairycode branch into master. Start testing.

This commit is contained in:
Uwe Brauer 2021-07-16 14:47:29 +02:00
commit 3fbc97b3f2
35 changed files with 5973 additions and 3306 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@
.\#*
*.orig
*.rej
*.dat
# matlab-load.el is generated
matlab-load.el
parse

View File

@ -16,7 +16,7 @@ LOADPATH= ./
LOADDEFS=matlab-load.el
LOADDIRS=.
misc_MISC=ChangeLog ChangeLog.old1 ChangeLog.old2 INSTALL README dl_emacs_support.m
lisp_LISP=matlab-compat.el matlab.el matlab-shell.el matlab-shell-gud.el matlab-netshell.el matlab-complete.el matlab-cgen.el matlab-publish.el matlab-topic.el mlint.el tlc.el linemark.el matlab-maint.el
lisp_LISP=matlab-compat.el matlab-syntax.el matlab-scan.el matlab.el matlab-shell.el matlab-shell-gud.el matlab-netshell.el matlab-complete.el matlab-cgen.el matlab-publish.el matlab-topic.el mlint.el tlc.el linemark.el matlab-maint.el
cedet_LISP=semantic-matlab.el semanticdb-matlab.el srecode-matlab.el cedet-matlab.el company-matlab-shell.el
VERSION=4.0
DISTDIR=$(top)matlab-emacs-$(VERSION)

138
NEWS.org
View File

@ -1,5 +1,141 @@
* Changes and New Features in matlab-emacs
** New in 5.0
*** Syntax tables / Strings and Comments / Font lock
Command and String syntax handling is now managed using syntax-table customization
This results in:
* More flavors of syntax highlighting around commands and strings, including all of:
* strings, unterminated strings, commanddual strings
* comments, cellbreak comments, pragma comments, ignored comments, ellipssis
* Accurate differentiation between 'char arrays' and "strings" and quoted charts.
* Performance improvements for comment/string parsing.
There is a new shorter 'ignore' comment type that starts with: %^
In addition, font lock of keywords is now more robust, with keywords not being
highlighted when they are not being used in the correct scope.
*** Syntactic block navigation
With proper syntax table support now available, built-in emacs commands that depend on
sexp now work, such as:
* up-list
* forward-sexp
* kill-sexp
* mark-sexp
In addition, commands that work with defuns now all work correctly, such as:
* mark-defun
* narrow-to-defun
All custom commands that used to implement these syntax behaviors have been removed, or
had their bindings removed, including:
* matlab-beginning-of-command
* matlab-end-of-command
* matlab-forward-sexp
* matlab-backward-sexp
* matlab-indent-sexp
* matlab-beginning-of-defun
* matlab-end-of-defn
In addition syntactic block navigation is faster, where very large files can now be navigated
in fractions of a second that used to take a few minutes.
*** Support for block validation
Block navigation now does validation, for example, 'property' keywords should only occur
inside a classdef, and 'arguments' keywords should only occur inside a function.
This means that you can now have variables and functions named 'property' and
'arguments' of those words occur outside valid locations.
*** Indentation
Indentation performance is greatly improved. Based on our tests, large files that used
to take 10 minutes to indent will now complete in just 1 or 2 seconds.
Several new indentation features exist, such as:
* correct indentation of arguemnts blocks
* improved indentation of function argument lists that span multiple lines.
* improved indentation around block comments
* improved indentation accuracy in classdef, property, method blocks.
* more accurate indentation of continuations
Some indentation features were removed, such as:
* Max indent distance support inside function call args
* Max indent distance support inside switch statements
* Line-up rules inside ( ), [ ], and { } have changed subtly dependeing on
context after the opening (, [, or {.
Specialty indentation commands have been removed:
* matlab-indent-sexp
Electric indentation has been added to block keywords such as end, else, case, etc.
Lots of bug fixes and general improvements for handling edge cases.
*** matlab-return & friends removed
The 'matlab-return' and related functions have all been removed. Features of these
commands are now part of Emacs' built in handling for RETURN and no longer need to be
part of matlab mode.
*** File type detection
File type detection has been improved. Previously matlab mode detected if functions had
ends, and if functions were indented. It now detects more cases, and displays results
in the status line.
The list of detectable features are:
* function (with no end)
* function .. end
* classdef .. end
* scripts
* empty files
Functions with ends also detect if function bodies are indented. Other kinds of
functions will always indent.
The check for the type of file is also auto-re-detected on save, so if you change the
type of file while editing, it will automatically adjust.
*** Auto verify changes
Auto verify on save has been updated.
1. verify classname added - this will fix class names for you
2. verify add ends - this now asks questions less often, or not at all.
In addition, it has a more robust algorithm for adding ends.
*** mlint support questions
mlint mode now supports many more auto fix behaviors, including:
* missing ends - with nicer guess for where the end goes.
* function name - auto fix function, class, and method names.
Plus several minor bug fixes.
*** Support for older Emacsen
Support for Emacs older than Emacs 24 has been dropped. Many of the special
compatability layers needed to change, and the new code has not been tested against
older versions of Emacs. As a result, many compatability layers were removed.
*** Test suite improvements
The test suite that is part of matlab-emacs project has many more test points, and has
added support for testing font lock, performance, and other features.
*** matlab-emacs maintenance mode
There is now a support file 'matlab-maint' that simplifies the task of building and
testing matlab-mode during development. Consider using this library if you intend to
develop matlab-mode.
** News in 4.0
*** Debugging
@ -56,7 +192,7 @@ You have the following capabilities when in a MATLAB M-file:
more responsive. For example, in matlab-shell, Emacs is more
responsive when processing long output lines from MATLAB.
*** Bug fixex
*** Bug fixes
- There are a number of bug fixes.
*** Quotes

View File

@ -17,7 +17,7 @@
(ede-proj-target-elisp "ede-proj-target-elisp"
:name "lisp"
:path ""
:source '("matlab-compat.el" "matlab.el" "matlab-shell.el" "matlab-shell-gud.el" "matlab-netshell.el" "matlab-complete.el" "matlab-cgen.el" "matlab-publish.el" "matlab-topic.el" "mlint.el" "tlc.el" "linemark.el" "matlab-maint.el")
:source '("matlab-compat.el" "matlab-syntax.el" "matlab-scan.el" "matlab.el" "matlab-shell.el" "matlab-shell-gud.el" "matlab-netshell.el" "matlab-complete.el" "matlab-cgen.el" "matlab-publish.el" "matlab-topic.el" "mlint.el" "tlc.el" "linemark.el" "matlab-maint.el")
:versionsource '("matlab.el")
:aux-packages '("matlab"))
(ede-proj-target-elisp "ede-proj-target-elisp"

View File

@ -275,7 +275,12 @@ Call the new entrie's activate method."
(oset new-entry face (or face (oref g face)))
(oset g marks (cons new-entry (oref g marks)))
(if (oref g active)
(linemark-display new-entry t))
(condition-case nil
;; Somewhere in the eieio framework this can throw 'end of buffer' error
;; after the display function exits. Not sure where that is, but this
;; condition-case can capture it and allow things to keep going.
(linemark-display new-entry t)
(error nil)))
new-entry)
))
@ -356,7 +361,12 @@ Call the new entrie's activate method."
(defun linemark-find-file-hook ()
"Activate all marks which can benifit from this new buffer."
(mapcar (lambda (g) (linemark-display g t)) linemark-groups))
(mapcar (lambda (g) (condition-case nil
;; See comment in linemark-add-entry for
;; reasoning on this condition-case.
(linemark-display g t)
(error nil)))
linemark-groups))
(defun linemark-kill-buffer-hook ()
"Deactivate all entries in the current buffer."

View File

@ -77,7 +77,9 @@
"Insert and END block based on the current syntax.
Optional argument REINDENT indicates if the specified block should be re-indented."
(interactive "P")
(if (not (matlab-ltype-empty)) (progn (end-of-line) (insert "\n")))
(when (not (matlab-line-empty-p (matlab-compute-line-context 1)))
(end-of-line) (insert "\n"))
(let ((valid t) (begin nil))
(save-excursion
(condition-case nil
@ -172,7 +174,8 @@ Optional argument REINDENT indicates if the specified block should be re-indente
(error (setq valid nil))))
(if (not valid)
(error "Not in a switch statement")))
(if (not (matlab-ltype-empty)) (progn (end-of-line) (insert "\n")))
(when (not (matlab-line-empty-p (matlab-compute-line-context 1)))
(end-of-line) (insert "\n"))
(indent-to 0)
(insert "case ")
(matlab-indent-line))
@ -234,14 +237,16 @@ the region. BEGIN and END mark the region to be stringified."
"Spell check valid strings in region with Ispell.
Argument BEGIN and END mark the region boundary."
(interactive "r")
(error "This function needs to be reimplemented.")
(require 'ispell)
(save-excursion
(goto-char begin)
;; Here we use the font lock function for finding strings.
;; Its cheap, fast, and accurate.
;; NOTE: This now also does comments
(while (and (matlab-font-lock-allstring-comment-match-normal end)
(ispell-region (match-beginning 0) (match-end 0))))))
;;(while (and (matlab-font-lock-allstring-comment-match-normal end)
;; (ispell-region (match-beginning 0) (match-end 0))))
))
(defun matlab-ispell-strings-and-comments ()
"Spell check valid strings in the current buffer with Ispell.

View File

@ -150,6 +150,11 @@ REGEXP defaults to \"[ \\t\\n\\r]+\"."
out))
)
;; OBARRAYS
(if (fboundp 'obarray-make)
(defalias 'matlab-obarray-make 'obarray-make)
(defun matlab-obarray-make (sz) (make-vector sz 0)))
;; Finding executables
(defun matlab-find-executable-directory (program)
"Find the executable PROGRAM on the exec path, following any links.

View File

@ -30,7 +30,21 @@
;; and this can be loaded optionally.
;;; Code:
(require 'cl-macs)
(require 'matlab)
(require 'matlab-shell)
(defun matlab-uniquify-list (lst)
"Return a list that is a subset of LST where all elements are unique."
(if (fboundp 'cl-remove-duplicates)
(cl-remove-duplicates lst :test 'string= :from-end t)
;; Else, do it by hand.
(let ((nlst nil))
(while lst
(if (and (car lst) (not (member (car lst) nlst)))
(setq nlst (cons (car lst) nlst)))
(setq lst (cdr lst)))
(nreverse nlst))))
;;; Customizations ===========================================================
;;
@ -181,9 +195,32 @@ This list still needs lots of help.")
tl (cdr tl)))
r))
(defun matlab-lattr-semantics (&optional prefix)
"Return the semantics of the current position.
Values are nil 'solo, 'value, and 'boolean. Boolean is a subset of
value. nil means there is no semantic content (ie, string or comment.)
If optional PREFIX, then return 'solo if that is the only thing on the
line."
(cond
((or (matlab-line-empty-p (matlab-compute-line-context 1))
(and prefix (save-excursion
(beginning-of-line)
(looking-at (concat "\\s-*" prefix "\\s-*$")))))
'solo)
((save-excursion
(matlab-beginning-of-command)
(looking-at "\\s-*\\(if\\|elseif\\|while\\)\\>"))
'boolean)
((save-excursion
(matlab-beginning-of-command)
(looking-at (concat "\\s-*\\(" (matlab-property-function)
"\\)\\>")))
'property)
(t
'value)))
;;; Completion Framework ===================================================
;;
(defun matlab-find-recent-variable-list (prefix)
"Return a list of most recent variables starting with PREFIX as a string.
Reverse searches for the following are done first:
@ -211,28 +248,28 @@ If the list is empty, then searches continue backwards through the code."
(nreverse lst)))
(save-excursion
(let ((lst nil))
(while (and (re-search-backward
(concat "\\<\\(" matlab-block-beg-pre-no-if
"\\)\\s-+(?\\s-*\\(" prefix
"\\w+\\)\\>")
bounds t)
(< (length lst) 10))
(setq lst (cons (match-string 2) lst)))
(while (and
(< (length lst) 10)
(matlab-re-search-keyword-backward (matlab-keyword-regex 'ctrl) bounds t))
(when (looking-at (concat "\\w+\\s-+(?\\(" prefix "\\w+\\)\\_>"))
(setq lst (cons (match-string 1) lst))))
(nreverse lst)))
(save-excursion
(if (re-search-backward "^\\s-*global\\s-+" bounds t)
(let ((lst nil) m e)
(let ((lst nil) m e)
(while (matlab-re-search-keyword-backward
(matlab-keyword-regex 'vardecl) bounds t)
(save-excursion
(goto-char (match-end 0))
(while (looking-at "\\(\\w+\\)\\([ \t]+\\|$\\)")
(while (looking-at "\\s-*\\(\\w+\\)\\([ \t]+\\|$\\)")
(setq m (match-string 1)
e (match-end 0))
(if (equal 0 (string-match prefix m))
(setq lst (cons m lst)))
(goto-char e))
(nreverse lst))))
(goto-char e))))
(nreverse lst)))
(save-excursion
(if (and (re-search-backward "^\\s-*function\\>" bounds t)
(re-search-forward "\\<\\(\\w+\\)("
(re-search-forward "\\_<\\(\\w+\\)\\s-*("
(matlab-point-at-eol) t))
(let ((lst nil) m e)
(while (looking-at "\\(\\w+\\)\\s-*[,)]\\s-*")
@ -278,7 +315,7 @@ In NEXT is non-nil, than continue through the list of elements."
(let ((lst nil))
(while (re-search-forward "^\\s-*function\\>" nil t)
(if (re-search-forward
(concat "\\(" prefix "\\w+\\)\\s-*\\($\\|(\\)")
(concat "\\_<\\(" prefix "\\w+\\)\\s-*\\($\\|(\\)")
(matlab-point-at-eol) t)
(setq lst (cons (match-string 1) lst))))
(nreverse lst)))
@ -426,7 +463,33 @@ Use `completion-in-region' to support the completion behavior."
(completion-in-region common-substr-start-pt common-substr-end-pt completions)
))
)
(defun matlab--complete-compute-search-functions (semantics)
"Return the search functions for context specified by SEMATNICS."
(cond ((eq semantics 'solo)
'(matlab-solo-completions
matlab-find-user-functions
matlab-find-recent-variable))
((eq semantics 'boolean)
'(matlab-find-recent-variable
matlab-boolean-completions
matlab-find-user-functions
matlab-value-completions))
((eq semantics 'value)
'(matlab-find-recent-variable
matlab-find-user-functions
matlab-value-completions
matlab-boolean-completions))
((eq semantics 'property)
'(matlab-property-completions
matlab-find-user-functions
matlab-find-recent-variable
matlab-value-completions))
(t '(matlab-find-recent-variable
matlab-find-user-functions
matlab-value-completions
matlab-boolean-completions))))
(defun matlab-complete-symbol-local (&optional arg)
"Complete a partially typed symbol in a MATLAB mode buffer.
If the previously entered command was also `matlab-complete-symbol'
@ -459,29 +522,7 @@ to change it temporarily."
(setq matlab-last-prefix prefix
matlab-last-semantic sem
matlab-completion-search-state
(cond ((eq sem 'solo)
'(matlab-solo-completions
matlab-find-user-functions
matlab-find-recent-variable))
((eq sem 'boolean)
'(matlab-find-recent-variable
matlab-boolean-completions
matlab-find-user-functions
matlab-value-completions))
((eq sem 'value)
'(matlab-find-recent-variable
matlab-find-user-functions
matlab-value-completions
matlab-boolean-completions))
((eq sem 'property)
'(matlab-property-completions
matlab-find-user-functions
matlab-find-recent-variable
matlab-value-completions))
(t '(matlab-find-recent-variable
matlab-find-user-functions
matlab-value-completions
matlab-boolean-completions)))))
(matlab--complete-compute-search-functions sem)))
(cond
((eq matlab-completion-technique 'increment)
(let ((r nil) (donext (eq last-command 'matlab-complete-symbol)))

View File

@ -24,6 +24,8 @@
(require 'matlab)
(require 'matlab-shell)
(require 'matlab-netshell)
(require 'semantic/symref)
(require 'semantic/symref/list)
;;; Code:
@ -31,8 +33,14 @@
;;
(defvar matlab-maint-mode-map
(let ((km (make-sparse-keymap)))
;; compile debug cycle
(define-key km [f8] 'matlab-maint-run-tests)
(define-key km [f9] 'matlab-maint-compile-matlab-emacs)
(define-key km [f10] 'matlab-maint-reload-mode)
;; coding
(define-key km [f7] 'matlab-maint-symref-this)
;; matlab
(define-key km [f5] 'matlab-maint-show-info)
km)
"Keymap used by matlab mode maintainers.")
@ -67,7 +75,35 @@
(matlab-maint-minor-mode 1))))
)
;;; Commands
;;; Testing stuff in M buffers
;;
(defun matlab-maint-show-info ()
"Show info about line in current matlab buffer."
(interactive)
(when (eq major-mode 'matlab-mode)
(matlab-show-line-info)
))
;;; HACKING ELISP
;;
(defun matlab-maint-symref-this ()
"Open a symref buffer on symbol under cursor."
(interactive)
(save-buffer)
(semantic-fetch-tags)
(let ((ct (semantic-current-tag)))
;; Must have a tag...
(when (not ct) (error "Place cursor inside tag to be searched for"))
;; Gather results and tags
(message "Gathering References for %s.." (semantic-tag-name ct))
(let* ((name (semantic-tag-name ct))
(res (semantic-symref-find-references-by-name name)))
(semantic-symref-produce-list-on-results res name)
(semantic-symref-list-expand-all) )))
;;; COMPILE AND TEST
;;
;; Helpful commands for maintainers.
(defcustom matlab-maint-compile-opts '("emacs" "emacs24" "emacs25" "emacs26")
@ -112,6 +148,8 @@
"Run the tests for matlab mode.
With universal ARG, ask for the code to be run with output tracking turned on."
(interactive "P")
(when (buffer-file-name)
(save-buffer))
(save-excursion
(matlab-maint-set-buffer-to "tests/Makefile")
(if (or arg matlab-shell-io-testing)
@ -123,6 +161,21 @@ With universal ARG, ask for the code to be run with output tracking turned on."
(delete-other-windows)
(goto-char (point-max)))
(defun matlab-maint-reload-mode ()
"Reload matlab mode, and refresh displayed ML buffers modes."
(interactive)
(load-library "matlab-syntax")
(load-library "matlab-scan")
(load-library "matlab")
(mapc (lambda (b) (with-current-buffer b
(when (eq major-mode 'matlab-mode)
(message "Updating matlab mode in %S" b)
(matlab-mode))))
(buffer-list (selected-frame)))
(message "loading done")
)
;;; MATLAB SHELL tools
;;
(defun matlab-maint-set-buffer-to (file)
"Set the current buffer to FILE found in matlab-mode's source.

1476
matlab-scan.el Normal file

File diff suppressed because it is too large Load Diff

View File

@ -89,7 +89,6 @@ Disable this option if the tooltips are too slow in your setup."
;; (gud-def gud-print "% gud-print not available" "\C-p" "gud-print not available.")
(when window-system
(matlab-frame-init)
(setq gud-matlab-tool-bar-map
(let ((map (make-sparse-keymap)))
@ -373,10 +372,10 @@ LONGESTNAME specifies the how long the longest name we can expect is."
"Specify a NEWSTACK provided by MATLAB to replace the old one."
(setq mlg-stack nil)
(dolist (L newstack)
(push (mlg-stack-frame ""
:file (nth 0 L)
:name (nth 1 L)
:line (nth 2 L))
(push (make-instance 'mlg-stack-frame
:file (nth 0 L)
:name (nth 1 L)
:line (nth 2 L))
mlg-stack))
(setq mlg-stack (nreverse mlg-stack))
(mlg-refresh-stack-buffer)
@ -598,9 +597,10 @@ LONGESTNAME specifies the how long the longest name we can expect is."
(setq found t)))
(when (not found)
(setq matlab-gud-visible-breakpoints
(cons (mlg-breakpoint "" :file file
:name fcn
:line line)
(cons (make-instance 'mlg-breakpoint
:file file
:name fcn
:line line)
matlab-gud-visible-breakpoints))
(mlg-activate (car matlab-gud-visible-breakpoints))
))

View File

@ -204,10 +204,27 @@ If multiple prompts are seen together, only call this once.")
;;; Font Lock
;;
;; Extra font lock keywords for the MATLAB shell.
(defvar matlab-shell-font-lock-keywords
(defconst matlab-shell-font-lock-keywords
(list
;; How about Errors?
'("^\\(Error in\\|Syntax error in\\)\\s-+==>\\s-+\\(.+\\)$"
(1 font-lock-comment-face) (2 font-lock-string-face))
;; and line numbers
'("^\\(\\(On \\)?line [0-9]+\\)" 1 font-lock-comment-face)
;; User beep things
'("\\(\\?\\?\\?[^\n]+\\)" 1 font-lock-comment-face)
)
"Additional keywords used by MATLAB when reporting errors in interactive\
mode.")
(defconst matlab-shell-font-lock-keywords-1
(append matlab-basic-font-lock-keywords
matlab-shell-font-lock-keywords)
"Keyword symbol used for basic font-lock for MATLAB shell.")
(defconst matlab-shell-object-output-font-lock-keywords
(list
;; Startup notices
;; Various notices
'(" M A T L A B " 0 'underline)
'("All Rights Reserved" 0 'italic)
'("\\(\\(?:(c)\\)?\\s-+Copyright[^\n]+\\)" 1 font-lock-comment-face)
@ -218,33 +235,38 @@ If multiple prompts are seen together, only call this once.")
'("^To get started, type doc.$" 0 font-lock-comment-face prepend)
'("For product information, [^\n]+" 0 font-lock-comment-face)
;; How about Errors?
'("^\\(Error in\\|Syntax error in\\)\\s-+==>\\s-+\\(.+\\)$"
(1 font-lock-comment-face) (2 font-lock-string-face))
;; and line numbers
'("^\\(\\(On \\)?line [0-9]+\\)" 1 font-lock-comment-face)
;; User beep things
'("\\(\\?\\?\\?[^\n]+\\)" 1 font-lock-comment-face)
;; Useful user commands, but not useful programming constructs
'("\\<\\(demo\\|whatsnew\\|info\\|subscribe\\|help\\|doc\\|lookfor\\|what\
\\|whos?\\|cd\\|clear\\|load\\|save\\|helpdesk\\|helpwin\\)\\>"
1 font-lock-keyword-face)
;; disp of objects usually looks like this:
'("^\\s-*\\(\\w+\\) with properties:" (1 font-lock-type-face))
;; object output - highlight property names after 'with properties:' indicator
;; NOTE: Normally a block like this would require us to use `font-lock-multiline' feature
;; but since this is shell output, and not a thing you edit, we can skip it and rely
;; on matlab-shell dumping the text as a unit.
'("^\\s-*\\(\\w+ with properties:\\)\n\\s-*\n"
("^\\s-*\\(\\w+\\):[^\n]+$" ;; match the property before the :
;; Extend search region across lines.
(save-excursion (re-search-forward "\n\\s-*\n" nil t)
(beginning-of-line)
(point))
nil
(1 font-lock-variable-name-face)))
'("[[{]\\([0-9]+\\(?:x[0-9]+\\)+ \\w+\\)[]}]" (1 font-lock-comment-face))
)
"Additional keywords used by MATLAB when reporting errors in interactive\
mode.")
"Highlight various extra outputs that are typical for MATLAB.")
(defvar matlab-shell-font-lock-keywords-1
(append matlab-font-lock-keywords matlab-shell-font-lock-keywords)
"Keyword symbol used for font-lock mode.")
(defconst matlab-shell-font-lock-keywords-2
(append matlab-shell-font-lock-keywords-1
matlab-function-font-lock-keywords
matlab-shell-object-output-font-lock-keywords)
"Keyword symbol used for gaudy font-lock for MATLAB shell.")
(defvar matlab-shell-font-lock-keywords-2
(append matlab-shell-font-lock-keywords-1 matlab-gaudy-font-lock-keywords)
"Keyword symbol used for gaudy font-lock symbols.")
(defvar matlab-shell-font-lock-keywords-3
(defconst matlab-shell-font-lock-keywords-3
(append matlab-shell-font-lock-keywords-2
matlab-really-gaudy-font-lock-keywords)
"Keyword symbol used for really gaudy font-lock symbols.")
"Keyword symbol used for really gaudy font-lock for MATLAB shell.")
;;; ROOT
;;
@ -1575,16 +1597,9 @@ Snatched and hacked from dired-x.el"
(search-forward-regexp comint-prompt-regexp)
(buffer-substring (point) (matlab-point-at-eol)))))
(save-excursion
;; In matlab buffer, find all the text for a command.
;; so back over until there is no more continuation.
(while (save-excursion (forward-line -1) (matlab-lattr-cont))
(forward-line -1))
;; Go forward till there is no continuation
(beginning-of-line)
(let ((start (point)))
(while (matlab-lattr-cont) (forward-line 1))
(end-of-line)
(buffer-substring start (point))))))
(buffer-substring-no-properties
(matlab-scan-beginning-of-command)
(matlab-scan-end-of-command)))))
(defun matlab-non-empty-lines-in-string (str)
"Return number of non-empty lines in STR."
@ -2164,7 +2179,13 @@ Similar to `comint-send-input'."
))
;; If not changing dir, maybe we need to use 'run' command instead?
(let ((cmd (concat "run('" dir fn-name "')")))
(let* ((match 0)
(tmp (while (setq match (string-match "'" param match))
(setq param (replace-match "''" t t param))
(setq match (+ 2 match))))
(cmd (concat "emacsrun('" dir fn-name "'"
(if (string= param "") "" (concat ", '" param "'"))
")")))
(matlab-shell-send-command cmd)))
))))
@ -2175,20 +2196,22 @@ Similar to `comint-send-input'."
(defun matlab-shell-run-cell ()
"Run the cell the cursor is in."
(interactive)
(let ((start (save-excursion (forward-page -1)
(if (looking-at "function")
(error "You are not in a cell. Try `matlab-shell-save-and-go' instead"))
(when (matlab-ltype-comm)
;; Skip over starting comment from the current cell.
(matlab-end-of-command 1)
(end-of-line)
(forward-char 1))
(point)))
(end (save-excursion (forward-page 1)
(when (matlab-ltype-comm)
(beginning-of-line)
(forward-char -1))
(point))))
(let ((start (save-excursion
(forward-page -1)
(if (looking-at "function")
(error "You are not in a cell. Try `matlab-shell-save-and-go' instead"))
(when (matlab-line-comment-p (matlab-compute-line-context 1))
;; Skip over starting comment from the current cell.
(matlab-end-of-command)
(end-of-line)
(forward-char 1))
(point)))
(end (save-excursion
(forward-page 1)
(when (matlab-line-comment-p (matlab-compute-line-context 1))
(beginning-of-line)
(forward-char -1))
(point))))
(matlab-shell-run-region start end t)))
(defun matlab-shell-run-region-or-line ()

573
matlab-syntax.el Normal file
View File

@ -0,0 +1,573 @@
;;; matlab-syntax.el --- Manage MATLAB syntax tables and buffer parsing.
;;
;; Copyright (C) 2021 Eric Ludlam
;;
;; Author: <eludlam@mathworks.com>
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see https://www.gnu.org/licenses/.
;;; Commentary:
;;
;; Manage syntax handling for `matlab-mode'.
;; Matlab's syntax for comments and strings can't be handled by a standard
;; Emacs syntax table. This code handles the syntax table, and special
;; scanning needed to augment a buffer's syntax for all our special cases.
;;
;; This file also handles all the special parsing needed to support indentation,
;; block scanning, and the line.
(require 'matlab-compat)
;;; Code:
(defvar matlab-syntax-support-command-dual t
"Non-nil means to support command dual for indenting and syntax highlight.
Does not work well in classes with properties with datatypes.")
(make-variable-buffer-local 'matlab-syntax-support-command-dual)
(put 'matlab-syntax-support-command-dual 'safe-local-variable #'booleanp)
(defvar matlab-syntax-table
(let ((st (make-syntax-table (standard-syntax-table))))
;; Comment Handling:
;; Multiline comments: %{ text %}
;; Single line comments: % text (single char start)
;; Ellipsis omments: ... text (comment char is 1st char after 3rd dot)
;; ^ handled in `matlab--syntax-propertize'
(modify-syntax-entry ?% "< 13" st)
(modify-syntax-entry ?{ "(} 2c" st)
(modify-syntax-entry ?} "){ 4c" st)
(modify-syntax-entry ?\n ">" st)
;; String Handling:
;; Character vector: 'text'
;; String: "text"
;; These next syntaxes are handled with `matlab--syntax-propertize'
;; Transpose: varname'
;; Quoted quotes: ' don''t ' or " this "" "
;; Unterminated Char V: ' text
(modify-syntax-entry ?' "\"" st)
(modify-syntax-entry ?\" "\"" st)
;; Words and Symbols:
(modify-syntax-entry ?_ "_" st)
;; Punctuation:
(modify-syntax-entry ?\\ "." st)
(modify-syntax-entry ?\t " " st)
(modify-syntax-entry ?+ "." st)
(modify-syntax-entry ?- "." st)
(modify-syntax-entry ?* "." st)
(modify-syntax-entry ?/ "." st)
(modify-syntax-entry ?= "." st)
(modify-syntax-entry ?< "." st)
(modify-syntax-entry ?> "." st)
(modify-syntax-entry ?& "." st)
(modify-syntax-entry ?| "." st)
;; Parentheticl blocks:
;; Note: these are in standard syntax table, repeated here for completeness.
(modify-syntax-entry ?\( "()" st)
(modify-syntax-entry ?\) ")(" st)
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
;;(modify-syntax-entry ?{ "(}" st) - Handled as part of comments
;;(modify-syntax-entry ?} "){" st)
st)
"MATLAB syntax table")
(defvar matlab-navigation-syntax-table
(let ((st (copy-syntax-table matlab-syntax-table)))
;; Make _ a part of words so we can skip them better
(modify-syntax-entry ?_ "w" st)
st)
"The syntax table used when navigating blocks.")
(defmacro matlab-navigation-syntax (&rest forms)
"Set the current environment for syntax-navigation and execute FORMS."
(declare (indent 0))
(list 'let '((oldsyntax (syntax-table))
(case-fold-search nil))
(list 'unwind-protect
(list 'progn
'(set-syntax-table matlab-navigation-syntax-table)
(cons 'progn forms))
'(set-syntax-table oldsyntax))))
(add-hook 'edebug-setup-hook
(lambda ()
(def-edebug-spec matlab-navigation-syntax def-body)))
;;; Buffer Scanning for Syntax Table Augmentation
;;
;; To support all our special syntaxes via syntax-ppss (parse partial
;; sexp), we need to scan the buffer for patterns, and then leave
;; behind the hints pps needs to do the right thing.
;;
;; Support is broken up in these functions:
;; * matlab--put-char-category - Apply a syntax category to a character
;; * matlab--syntax-symbol - Create a syntax category symbol
;; * matlab--syntax-propertize - Used as `syntax-propertize-function' for
;; doing the buffer scan to augment syntxes.
;; * matlab--scan-line-* - Scan for specific types of syntax occurances.
(defun matlab--put-char-category (pos category)
"At character POS, put text CATEGORY."
(when (not (eobp))
(put-text-property pos (1+ pos) 'category category)
(put-text-property pos (1+ pos) 'mcm t))
)
(defmacro matlab--syntax-symbol (symbol syntax doc)
"Create a new SYMBOL used as a text property category with SYNTAX."
(declare (indent defun))
`(progn (defvar ,symbol ,syntax ,doc)
(set ',symbol ,syntax) ;; So you can re-eval it.
(put ',symbol 'syntax-table ,symbol)
))
(matlab--syntax-symbol matlab--command-dual-syntax '(15 . nil) ;; Generic string
"Syntax placed on end-of-line for unterminated strings.")
(put 'matlab--command-dual-syntax 'command-dual t) ;; Font-lock cookie
(matlab--syntax-symbol matlab--unterminated-string-syntax '(15 . nil) ;; Generic string end
"Syntax placed on end-of-line for unterminated strings.")
(put 'matlab--unterminated-string-syntax 'unterminated t) ;; Font-lock cookie
(matlab--syntax-symbol matlab--ellipsis-syntax (string-to-syntax "< ") ;; comment char
"Syntax placed on ellipsis to treat them as comments.")
(matlab--syntax-symbol matlab--not-block-comment-syntax (string-to-syntax "(}") ;; Just a regular open brace
"Syntax placed on ellipsis to treat them as comments.")
(defun matlab--syntax-propertize (&optional start end)
"Scan region between START and END for unterminated strings.
Only scans whole-lines, as MATLAB is a line-based language.
If region is not specified, scan the whole buffer.
See `matlab--scan-line-for-ellipsis', `matlab--san-line-bad-blockcomment',
and `matlab--scan-line-for-unterminated-string' for specific details."
(save-match-data ;; avoid 'Syntax Checking transmuted the match-data'
(save-excursion
;; Scan region, but always expand to beginning of line
(goto-char (or start (point-min)))
(beginning-of-line)
;; Clear old properties
(remove-text-properties (point) (save-excursion (goto-char (or end (point-max)))
(end-of-line) (point))
'(category nil mcm nil))
;; Apply properties
(while (and (not (>= (point) (or end (point-max)))) (not (eobp)))
(when matlab-syntax-support-command-dual
;; Commandl line dual comes first to prevent wasting time
;; in later checks.
(beginning-of-line)
(when (matlab--scan-line-for-command-dual)
(matlab--put-char-category (point) 'matlab--command-dual-syntax)
(end-of-line)
(matlab--put-char-category (point) 'matlab--command-dual-syntax)
))
;; Multiple ellipsis can be on a line. Find them all
(beginning-of-line)
(while (matlab--scan-line-for-ellipsis)
;; Mark ellipsis as if a comment.
(matlab--put-char-category (point) 'matlab--ellipsis-syntax)
(forward-char 3)
)
;; Multiple invalid block comment starts possible. Find them all
(beginning-of-line)
(while (matlab--scan-line-bad-blockcomment)
;; Mark 2nd char as just open brace, not punctuation.
(matlab--put-char-category (point) 'matlab--not-block-comment-syntax)
)
;; Look for an unterminated string. Only one possible per line.
(beginning-of-line)
(when (matlab--scan-line-for-unterminated-string)
;; Mark this one char plus EOL as end of string.
(let ((start (point)))
(matlab--put-char-category (point) 'matlab--unterminated-string-syntax)
(end-of-line)
(matlab--put-char-category (point) 'matlab--unterminated-string-syntax)
))
(beginning-of-line)
(forward-line 1))
)))
(defconst matlab-syntax-commanddual-functions
'("warning" "disp" "cd"
;; debug
"dbstop" "dbclear"
;; Graphics
"print" "xlim" "ylim" "zlim" "grid" "hold" "box" "colormap" "axis")
"Functions that are commonly used with commandline dual")
(defconst matlab-cds-regex (regexp-opt matlab-syntax-commanddual-functions 'symbols))
(defun matlab--scan-line-for-command-dual (&optional debug)
"Scan this line for command line duality strings."
;; Note - add \s$ b/c we'll add that syntax to the first letter, and it
;; might still be there during an edit!
(let ((case-fold-search nil))
(when (and (not (nth 9 (syntax-ppss (point))))
(looking-at
(concat "^\\s-*"
matlab-cds-regex
"\\s-+\\(\\s$\\|\\w\\|\\s_\\)")))
(goto-char (match-beginning 2)))))
(matlab--syntax-symbol matlab--transpose-syntax '(3 . nil) ;; 3 = symbol
"Treat ' as non-string when used as transpose.")
(matlab--syntax-symbol matlab--quoted-string-syntax '(9 . nil) ;; 9 = escape in a string
"Treat '' or \"\" as not string delimeteres when inside a string.")
(defun matlab--scan-line-for-unterminated-string (&optional debug)
"Scan this line for an unterminated string, leave cursor on starting string char."
;; First, scan over all the string chars.
(save-restriction
(narrow-to-region (point-at-bol) (point-at-eol))
(beginning-of-line)
(condition-case err
(while (re-search-forward "\\s\"\\|\\s<" nil t)
(let ((start-str (match-string 0))
(start-char (match-beginning 0)))
(forward-char -1)
(if (looking-at "\\s<")
(progn
(matlab--scan-line-comment-disable-strings)
(forward-comment 1))
;; Else, check for valid string
(if (or (bolp)
(string= start-str "\"")
(save-excursion
(forward-char -1)
(not (looking-at "\\(\\w\\|\\s_\\|\\s)\\|\"\\|\\.\\)"))))
(progn
;; Valid string start, try to skip the string
(forward-sexp 1)
;; If we just finished and we have a double of ourselves,
;; convert those doubles into punctuation.
(when (looking-at start-str)
(matlab--put-char-category (1- (point)) 'matlab--quoted-string-syntax)
;; and try again.
(goto-char start-char)
))
(when (string= start-str "'")
;; If it isn't valid string, it's just transpose or something.
;; convert to a symbol - as a VAR'', the second ' needs to think it
;; is not after punctuation.
(matlab--put-char-category (point) 'matlab--transpose-syntax))
;; Move forward 1.
(forward-char 1)
)))
nil)
(error
t))))
(defun matlab--scan-line-comment-disable-strings ()
"Disable bad string chars syntax from point to eol.
Called when comments found in `matlab--scan-line-for-unterminated-string'."
(save-excursion
(while (re-search-forward "\\s\"" nil t)
(save-excursion
(matlab--put-char-category (1- (point)) 'matlab--transpose-syntax)
))))
(defun matlab--scan-line-bad-blockcomment ()
"Scan this line for invalid block comment starts."
(when (and (re-search-forward "%{" (point-at-eol) t) (not (looking-at "\\s-*$")))
(goto-char (1- (match-end 0)))
t))
(defun matlab--scan-line-for-ellipsis ()
"Scan this line for an ellipsis."
(when (re-search-forward "\\.\\.\\." (point-at-eol) t)
(goto-char (match-beginning 0))
t))
;;; Font Lock Support:
;;
;; The syntax specific font-lock support handles comments and strings.
;;
;; We'd like to support multiple kinds of strings and comments. To do
;; that we overload `font-lock-syntactic-face-function' with our own.
;; This does the same job as the orriginal, except we scan the start
;; for special cookies left behind by `matlab--syntax-propertize' and
;; use that to choose different fonts.
(defun matlab--font-lock-syntactic-face (pps)
"Return the face to use for the syntax specified in PPS."
;; From the default in font-lock.
;; (if (nth 3 state) font-lock-string-face font-lock-comment-face)
(if (nth 3 pps)
;; This is a string. Check the start char to see if it was
;; marked as an unterminate string.
(cond ((get-text-property (nth 8 pps) 'unterminated)
'matlab-unterminated-string-face)
((get-text-property (nth 8 pps) 'command-dual)
'matlab-commanddual-string-face)
(t
'font-lock-string-face))
;; Not a string, must be a comment. Check to see if it is a
;; cellbreak comment.
(cond ((and (< (nth 8 pps) (point-max))
(= (char-after (1+ (nth 8 pps))) ?\%))
'matlab-cellbreak-face)
((and (< (nth 8 pps) (point-max))
(= (char-after (1+ (nth 8 pps))) ?\#))
'matlab-pragma-face)
((and (< (nth 8 pps) (point-max))
(looking-at "\\^\\| \\$\\$\\$"))
'matlab-ignored-comment-face)
(t
'font-lock-comment-face))
))
;;; SETUP
;;
;; Connect our special logic into a running MATLAB Mode
;; replacing existing mechanics.
;;
;; Delete this if/when it becomes a permanent part of `matlab-mode'.
(defun matlab-syntax-setup ()
"Integrate our syntax handling into a running `matlab-mode' buffer.
Safe to use in `matlab-mode-hook'."
;; Syntax Table support
(set-syntax-table matlab-syntax-table)
(make-local-variable 'syntax-propertize-function)
(setq syntax-propertize-function 'matlab--syntax-propertize)
;; Comment handlers
(make-local-variable 'comment-start)
(make-local-variable 'comment-end)
(make-local-variable 'comment-start-skip)
(make-local-variable 'page-delimiter)
(setq comment-start "%"
comment-end ""
comment-start-skip "%\\s-+"
page-delimiter "^\\(\f\\|%%\\(\\s-\\|\n\\)\\)")
;; Other special regexps handling different kinds of syntax.
(make-local-variable 'paragraph-start)
(setq paragraph-start (concat "^$\\|" page-delimiter))
(make-local-variable 'paragraph-separate)
(setq paragraph-separate paragraph-start)
(make-local-variable 'paragraph-ignore-fill-prefix)
(setq paragraph-ignore-fill-prefix t)
;; Font lock
(make-local-variable 'font-lock-syntactic-face-function)
(setq font-lock-syntactic-face-function 'matlab--font-lock-syntactic-face)
)
;;; Syntax Testing for Strings and Comments
;;
;; These functions detect syntactic context based on the syntax table.
(defsubst matlab-cursor-in-string-or-comment ()
"Return non-nil if the cursor is in a valid MATLAB comment or string."
(nth 8 (syntax-ppss (point))))
(defsubst matlab-cursor-in-comment ()
"Return t if the cursor is in a valid MATLAB comment."
(nth 4 (syntax-ppss (point))))
(defsubst matlab-cursor-in-string (&optional incomplete)
"Return t if the cursor is in a valid MATLAB character vector or string scalar.
Note: INCOMPLETE is now obsolete
If the optional argument INCOMPLETE is non-nil, then return t if we
are in what could be a an incomplete string. (Note: this is also the default)"
(nth 3 (syntax-ppss (point))))
(defun matlab-cursor-comment-string-context (&optional bounds-sym)
"Return the comment/string context of cursor for the current line.
Return 'comment if in a comment.
Return 'string if in a string.
Return 'charvector if in a character vector
Return 'ellipsis if after an ... ellipsis
Return 'commanddual if in text interpreted as string for command dual
Return nil if none of the above.
Scans from the beginning of line to determine the context.
If optional BOUNDS-SYM is specified, set that symbol value to the
bounds of the string or comment the cursor is in"
(let* ((pps (syntax-ppss (point)))
(start (nth 8 pps))
(end 0)
(syntax nil))
;; Else, inside something if 'start' is set.
(when start
(save-match-data
(save-excursion
(goto-char start) ;; Prep for extra checks.
(setq syntax
(cond ((eq (nth 3 pps) t)
(cond ((= (following-char) ?')
'charvector)
((= (following-char) ?\")
'string)
(t
'commanddual)))
((eq (nth 3 pps) ?')
'charvector)
((eq (nth 3 pps) ?\")
'string)
((nth 4 pps)
(if (= (following-char) ?\%)
'comment
'ellipsis))
(t nil)))
;; compute the bounds
(when (and syntax bounds-sym)
(if (memq syntax '(charvector string))
;;(forward-sexp 1) - overridden - need primitive version
(goto-char (scan-sexps (point) 1))
(forward-comment 1)
(if (bolp) (forward-char -1)))
(set bounds-sym (list start (point))))
)))
;; Return the syntax
syntax))
(defsubst matlab-beginning-of-string-or-comment (&optional all-comments)
"If the cursor is in a string or comment, move to the beginning.
Returns non-nil if the cursor is in a comment."
(let* ((pps (syntax-ppss (point))))
(prog1
(when (nth 8 pps)
(goto-char (nth 8 pps))
t)
(when all-comments (forward-comment -100000)))))
(defun matlab-end-of-string-or-comment (&optional all-comments)
"If the cursor is in a string or comment, move to the end.
If optional ALL-COMMENTS is non-nil, then also move over all
adjacent comments.
Returns non-nil if the cursor moved."
(let* ((pps (syntax-ppss (point)))
(start (point)))
(if (nth 8 pps)
(progn
;; syntax-ppss doesn't have the end, so go to the front
;; and then skip forward.
(goto-char (nth 8 pps))
(if (nth 3 pps)
(goto-char (scan-sexps (point) 1))
(forward-comment (if all-comments 100000 1)))
;; If the buffer is malformed, we might end up before starting pt.
;; so error.
(when (< (point) start)
(goto-char start)
(error "Error navitaging syntax."))
t)
;; else not in comment, but still skip 'all-comments' if requested.
(when (and all-comments (looking-at "\\s-*\\s<"))
(forward-comment 100000)
t)
)))
;;; Navigating Lists
;;
;; MATLAB's lists are (), {}, [].
;; We used to need to do special stuff, but now I think this
;; is just a call striaght to up-list.
(defun matlab-up-list (count)
"Move forwards or backwards up a list by COUNT.
When travelling backward, use `syntax-ppss' counted paren
starts to navigate upward.
When travelling forward, use 'up-list' diretly, but disable
comment and string crossing."
(save-restriction
(matlab-beginning-of-string-or-comment)
(if (< count 0)
(let ((pps (syntax-ppss)))
(when (< (nth 0 pps) (abs count))
(error "Cannot navigate up %d lists" (abs count)))
;; When travelling in reverse, we can just use pps'
;; parsed paren list in slot 9.
(let ((posn (reverse (nth 9 pps)))) ;; Location of parens
(goto-char (nth (1- (abs count)) posn))))
;; Else - travel forward
(up-list count nil t)) ;; will this correctly ignore comments, etc?
))
(defsubst matlab-in-list-p ()
"If the cursor is in a list, return positions of the beginnings of the lists.
Returns nil if not in a list."
(nth 9 (syntax-ppss (point))))
(defsubst matlab-beginning-of-outer-list ()
"If the cursor is in a list, move to the beginning of outermost list.
Returns non-nil if the cursor moved."
(let ((pps (syntax-ppss (point))))
(when (nth 9 pps) (goto-char (car (nth 9 pps))) )))
(defun matlab-end-of-outer-list ()
"If the cursor is in a list, move to the end of the outermost list..
Returns non-nil if the cursor moved."
(let ((pps (syntax-ppss (point)))
(start (point)))
(when (nth 9 pps)
;; syntax-ppss doesn't have the end, so go to the front
;; and then skip forward.
(goto-char (car (nth 9 pps)))
(goto-char (scan-sexps (point) 1))
;; This checks for malformed buffer content
;; that can cause this to go backwards.
(when (> start (point))
(goto-char start)
(error "Malformed List"))
)))
;;; Useful checks for state around point.
;;
(defsubst matlab-syntax-keyword-as-variable-p ()
"Return non-nil if the current word is treated like a variable.
This could mean it is:
* Field of a structure
* Assigned from or into with ="
(or (save-excursion (skip-syntax-backward "w")
(skip-syntax-backward " ")
(or (= (preceding-char) ?\.)
(= (preceding-char) ?=)))
(save-excursion (skip-syntax-forward "w")
(skip-syntax-forward " ")
(= (following-char) ?=))))
(defsubst matlab-valid-keyword-syntax ()
"Return non-nil if cursor is not in a string, comment, or parens."
(let ((pps (syntax-ppss (point))))
(not (or (nth 8 pps) (nth 9 pps))))) ;; 8 == string/comment, 9 == parens
;;; Syntax Compat functions
;;
;; Left over old APIs. Delete these someday.
(defsubst matlab-move-simple-sexp-backward-internal (count)
"Move backward COUNT number of MATLAB sexps."
(let ((forward-sexp-function nil))
(forward-sexp (- count))))
(defsubst matlab-move-simple-sexp-internal(count)
"Move over one MATLAB sexp COUNT times.
If COUNT is negative, travel backward."
(let ((forward-sexp-function nil))
(forward-sexp count)))
(provide 'matlab-syntax)
;;; matlab-syntax.el ends here

4878
matlab.el

File diff suppressed because it is too large Load Diff

100
mlint.el
View File

@ -188,8 +188,14 @@ be cause for being turned off in a buffer."
( NOPRT . mlint-lm-quiet )
( NOSEM . mlint-lm-delete-focus )
( NOCOM . mlint-lm-delete-focus )
( MSNU . mlint-lm-delete-focus )
( ST2NM . mlint-lm-str2num )
( FDEPR . mlint-lm-entry-deprecated )
( ENDCT . mlint-lm-missing-end )
( ENDCT2 . mlint-lm-missing-end )
( FNDEF . mlint-lm-function-name )
( MCFIL . mlint-lm-function-name )
( MCSCC . mlint-lm-function-name )
)
"List of warning IDs and auto-fix functions.
If the CAR of an association matches an error id then the linemark entry
@ -468,7 +474,7 @@ ACTIVE-P if it should be made visible."
"Return non-nil if entry E can be automatically fixed."
(oref-default e fixable-p))
(cl-defmethod mlint-fix-entry :AFTER ((e mlint-lm-entry))
(cl-defmethod mlint-fix-entry :after ((e mlint-lm-entry))
"Stuff to do after a warning is considered fixed.
Subclasses fulfill the duty of actually fixing the code."
(linemark-display e nil)
@ -497,6 +503,8 @@ Subclasses fulfill the duty of actually fixing the code."
)
(goto-char s)
(delete-region (point) e)
;; If this happened to be at end of line, just delete all left over whitespace.
(when (looking-at "\\s-*$") (delete-horizontal-space))
(point)
))
)
@ -508,13 +516,13 @@ Subclasses fulfill the duty of actually fixing the code."
"Class which can replace the focus area."
:abstract t)
(cl-defmethod initialize-instance :AFTER ((this mlint-lm-replace-focus)
(cl-defmethod initialize-instance :after ((this mlint-lm-replace-focus)
&rest fields)
"Calculate the new fix description for THIS.
Optional argument FIELDS are the initialization arguments."
;; After basic initialization, update the fix description.
(oset this fix-description
(concat (oref-default mlint-lm-replace-focus fix-description)
(concat (oref-default this fix-description)
(oref this new-text))))
(cl-defmethod mlint-fix-entry ((ent mlint-lm-replace-focus))
@ -533,7 +541,7 @@ Optional argument FIELDS are the initialization arguments."
"Entry for anything that is deprecated.
Extracts the replacement for the deprecated symbol from the warning message.")
(cl-defmethod initialize-instance :AFTER ((this mlint-lm-entry-deprecated)
(cl-defmethod initialize-instance :after ((this mlint-lm-entry-deprecated)
&rest fields)
"Calculate the 'new text' for THIS instance.
Optional argument FIELDS are the initialization arguments."
@ -544,10 +552,33 @@ Optional argument FIELDS are the initialization arguments."
(oset this new-text newfcn)
;; After basic initialization, update the fix description.
(oset this fix-description
(concat (oref-default mlint-lm-replace-focus fix-description)
(concat (oref-default this fix-description)
newfcn))
))
(defclass mlint-lm-function-name (mlint-lm-replace-focus)
()
"When function name is missmatched with the file name."
)
(cl-defmethod initialize-instance :after ((this mlint-lm-function-name) &rest fields)
"Compute the 'new text' for THIS to be the file name from the message.
Optional arguments FIELDS are the initialization arguments."
(let* ((warn (oref this warning))
(junk (or (string-match "file name: '\\([a-zA-z][a-zA-z0-9]+\\)'" warn)
(string-match "do not agree: '\\([a-zA-z][a-zA-z0-9]+\\)'" warn)
(string-match "of the subclass '\\([a-zA-z][a-zA-z0-9]+\\)'" warn))
)
(newfcn (when junk (match-string 1 warn))))
(oset this new-text newfcn)
;; After basic initialization, update the fix description.
(oset this fix-description
(concat (oref-default this fix-description)
newfcn))
))
;;; Custom auto-fix entries
;;
(defclass mlint-lm-entry-logicals (mlint-lm-entry)
((fixable-p :initform t)
(fix-description :initform "perform a replacement.")
@ -600,6 +631,53 @@ Optional argument FIELDS are the initialization arguments."
(insert ";"))
)
(defclass mlint-lm-missing-end (mlint-lm-entry)
((fixable-p :initform t)
(fix-description :initform "Add matching end for this line."))
"Missing end with guess as to where it might go."
)
(cl-defmethod mlint-fix-entry ((ent mlint-lm-missing-end))
"Add semi-colon to end of this line."
(save-excursion
(let* ((msg (oref ent warning))
line blockname)
;; Extract info about this.
(when (string-match "(after line \\([0-9]+\\))" msg)
(setq line (match-string 1 msg)))
(when (string-match "possibly matching \\([A-Z]+\\)\\." msg)
(setq blockname (match-string 1 msg)))
;; Did we get the right kind of warning
(if line
;; We have a line number, just go for it there.
(progn
(mlint-goto-line (string-to-number line))
;; add the end and indent
(indent-region (point) (save-excursion (insert "end\n") (point)))
)
(if (and blockname (string= blockname "FUNCTION"))
;; It is a function, but no line number. Let's guess where this end
;; should go.
(save-excursion
(mlint-goto-line (oref ent line)) ;; go to the fcn
(end-of-line)
(if (re-search-forward "^function " nil t)
(progn
(beginning-of-line)
;; skip over comments that might be headers to the found function.
(matlab-previous-command-begin
(matlab-compute-line-context 2)) ;;(matlab-find-prev-code-line)
(forward-line 1)
(save-excursion (insert "end\n\n"))
(matlab-indent-line))
(goto-char (point-max))
(save-excursion (insert "\nend\n\n"))
(matlab-indent-line))))
)
))
)
;;; User functions
;;
(defun mlint-highlight (err)
@ -740,10 +818,14 @@ Highlight problems and/or cross-function variables."
(if (not n)
(message "No warning at point.")
(let ((col (matlab-comment-on-line)))
(or col (end-of-line))
(insert " %#ok")
;; Add spaces if there was a comment.
(when col (insert " ")))
(if col
(progn
(goto-char col)
(skip-chars-forward "% ")
(insert "#ok "))
(end-of-line)
(insert " %#ok"))
)
;; This causes inconsistencies.
;; (linemark-delete n)
))

View File

@ -206,7 +206,7 @@ Return argument is:
(while (re-search-forward semantic-matlab-match-methods-block-re nil end)
(save-excursion ;; find end of properties block
(goto-char (match-beginning 0))
(matlab-forward-sexp nil nil)
(matlab-forward-sexp nil)
(setq tmpend (point)))
(setq attrs (semantic-matlab-parse-attributes-and-move))
@ -239,7 +239,7 @@ Return argument is:
(setq attrs (semantic-matlab-parse-attributes-and-move))
(save-excursion ;; find end of properties block
(matlab-forward-sexp nil t)
(matlab-forward-sexp t)
(beginning-of-line)
(setq tmpend (point)))

View File

@ -76,7 +76,7 @@
;; Create the database, and add it to searchable databases for matlab mode.
(defvar-mode-local matlab-mode semanticdb-project-system-databases
(list
(semanticdb-project-database-matlab "Matlab"))
(make-instance 'semanticdb-project-database-matlab))
"Search MATLAB path for symbols.")
;; NOTE: Be sure to modify this to the best advantage of your
@ -99,7 +99,7 @@ Create one of our special tables that can act as an intermediary."
;; We need to return something since there is always the "master table"
;; The table can then answer file name type questions.
(when (not (slot-boundp obj 'tables))
(let ((newtable (semanticdb-table-matlab "MATLAB system table")))
(let ((newtable (make-instance 'semanticdb-table-matlab)))
(oset obj tables (list newtable))
(oset newtable parent-db obj)
(oset newtable tags nil)
@ -337,8 +337,7 @@ Return a list of tags."
(let ((files (semanticdb-matlab-find-name regex 'regex)))
(delq nil
(mapcar #'(lambda (x)
(let ((matlab-vers-on-startup nil))
(car (semanticdb-file-stream x))))
(car (semanticdb-file-stream x)))
files)))))
(cl-defmethod semanticdb-find-tags-for-completion-method
@ -378,8 +377,7 @@ Returns a table of all matching tags."
;; generate tags
(delq nil
(mapcar #'(lambda (x)
(let ((matlab-vers-on-startup nil))
(car (semanticdb-file-stream x))))
(car (semanticdb-file-stream x)))
compdb)))))
(provide 'semanticdb-matlab)

View File

@ -20,8 +20,11 @@ VERSION=4.0
DISTDIR=$(top)matlab-emacs-$(VERSION)/tests
ifdef OS # No MATLAB shell tests on windows.
all: tests matlab modetests
else # The OS variable is only defined on windows, so linux systems can run shell tests.
all: tests matlab modetests shelltests
endif
.PHONY: modetests
modetests: metest.elc

213
tests/blocks.m Normal file
View File

@ -0,0 +1,213 @@
% >>1
classdef blocks < handle
% !!0
% >>11
properties %!!4
normalprop = 1; %!!8
end % <<11
%>>12
properties(Access='public') %!!4
% See if we can create properties using keywords
%properties = 1;
%methods = 1;
%events = 1;
arguments %!!8
prop = 1;
end %<<12
% >> 13
events (Access='private') %!!4
%properties
%events
%methods
arguments %#ok %!!8
misc %!!8
end % <<13
%>>14
methods %!!4
%>>15
function simple_method(obj) %!!8
%>>151
arguments %!!12
obj %!!16
end %<<151
disp(obj.normalprop);
end %<<15
%>>16
function obj = blocks(arguments,events,properties,methods,enumeration,normal)%!!8
%>>161
arguments %!!12
arguments%!!16
events%!!16
properties%!!16
methods%!!16
enumeration%!!16
normal%!!16
end %<<161
obj.prop = arguments;%!!12
obj.prop = events;%!!12
obj.prop = properties;%!!12
obj.prop = methods;%!!12
obj.prop = enumeration;%!!12
obj.prop = normal;%!!12
end %<<16
%>>17
function properties(~)%!!8
end %<<17
%>>18
function methods(~)%!!8
end %<<18
%>>19
function events(~)%!!8
end %<<19
%>>20
function events=arguments(arguments)%!!8
arguments, arguments(:,:) {mustBeNumeric}, end %!!12
%^ ^kw ^vn ^ty ^df ^kw ^co
enumeration ... %!!12
...%^ ^df
= arguments;
%^ ^df
if enumeration > 0 %!!12
arguments = -enumeration; %!!16
%^ ^df ^bi ^df
end %!!12
events ... %!!12
= arguments + 1;
%^ ^df
end %<<20
%>>21
function enumeration(~)%!!8
%^ ^fn
end %<<21
function y = multiple_arg_blocks(a, b, varargin) %!!8
arguments %!!12
%^ ^kw
a uint32 %!!16
%^ ^vn ^ty
b uint32 %!!16
%^ ^vn ^ty
end %!!12
%^ ^kw
arguments (Repeating) %!!12
%^ ^kw ^ty
varargin %!!16
%^ ^vn
end %!!12
%^ ^kw
y = a+b+length(varargin); %!!12
end
function linLog(x,y,scale)
arguments(Repeating) %!!12
x (1,:) double
y (1,:) double
end %!!12
arguments %!!12
scale.Plottype(1,1) string %!!16
%^ ^vn ^vn ^ty ^ty
end %!!12
sprintf('%d %d %s\n', x, y, scale);
end
%>>22
function usestuff(obj)%!!8
% Try using the methods of this object
obj.properties();%!!12
obj.methods();%!!12
obj. events();%!!12
obj. arguments();%!!12
obj. enumeration();%!!12
normal();%!!12
end %<<22
%>>23
function [m,s] = blarg(~, arguments)%!!8
% Starter comments.
%>>231
arguments%!!12
~%!!16
arguments(:,:) {mustBeNumeric}%!!16
end %<<231
m = mean( ...%!!12
arguments, 'all'); %!!16
arguments = 1;%!!12
events = arguments;%!!12
methods = events;%!!12
properties = methods;%!!12
enumeration = properties;%!!12
s = enumeration;%!!12
end %<<23
%>>24
function s = simple(~,arguments)%!!8
% Simple function
events = arguments;%!!12
methods = events;%!!12
properties = methods;%!!12
enumeration = properties;%!!12
s = enumeration; %!!12
end %<<24
function methods=foo3(obj,properties) %!!8
methods=obj.arguments(properties); %!!12
end %!!8
function s=struct_stuff(~) %!!8
s.if = 1; %!!12
s.else = 1.5; %!!12
s.while = 2; %!!12
s.switch = 3; %!!12
s.case = 3.1; %!!12
s.end = 5; %!!12
end %!!8
function tightcomments(~)%!!8
if condition%!!12
switch thing %!!16
case b %!!18
end%!!16
end%!!12
end%!!8
%!!8
end %<<14
%!!4
end % <<1
%!!0

47
tests/complete.m Normal file
View File

@ -0,0 +1,47 @@
function complete (a_arg1, b_arg2)
% This function is for testing completion tools
arguments
a_arg1 (1,1) double
b_arg2 (2,1) int
end
global a_global
global b_global
a_localvar = 1;
b_localvar = 1;
for a_for=1:10
end
if bool_var
end
switch a_switch
end
a_
% @@(solo var "a_localvar" "a_switch" "a_for" "a_global" "a_arg1")
% @@(solo fcn )
% Note: For b, there are other test files the completion
% engine will find, so they are included.
b
% @@(solo fcn "blocal_fcn" "blazy_fcn" "buggy" "blocks")
% @@(solo var "b_localvar" "bool_var" "b_global" "b_arg2")
% quiet mlint
blocal_fcn(a_arg1, b_arg2, a_localvar, b_localvar, a_global, b_global);
end
function blocal_fcn(varargin)
blazy_fcn(varargin{:});
end
function blazy_fcn(varargin)
end

184
tests/continuations.m Normal file
View File

@ -0,0 +1,184 @@
function continuations(a,b) %!!0
% !!0 Help Comment
arguments (Repeating)
a (1,1) ... % !!8
double % !!12
b (1,1) double { mustBeSomething ... %!!8
mustBeSomethingElse } %!!25
end
global var1, ... % !!4
var2 % !!8
localfcn(a,b); % !!4
localfcn(var1, ... % !!4
var2); % !!13
localfcn(a, localfcn(b, localfcn(var1,... %!!4
var2)... %!37
)... %!!24
); %!!12
code1(), ...
code2(); %!!8
% NOTE: Blank space below should cancel the indent effect of ellipsis.
code1() ...
var1 = 1 + ... %!!4
... %!!11
2 + ... %!!11
3; %!!11
medvar = 1 + ... %!!4
2; %!!13
long_var_name = 1 + ... %!!4
2; %!!8
localfcn (medvar, ... %!!4
long_var_name); %!!19
localfcn( ... %!!4
a,b); %!!8
if true, if true, if true %#ok %!!4
localfcn(a,b); ... %!!16
end; ... %!!4
end; ... %!!4
end ... %!!4
odd_if_location(); %!!4 - this after those continued ends
... % !!4 A continuation with a ctrl block after it
for g=1:10 %#ok % !!8 b/c continuation
localfcn(a,g) % !!8 b/c not continued, and +4 from comment continuation
end % !!4 to match
% !!4 to undo continuation.
% Indent past 1st arg for special functions
set(myhandle, 'Prop1', value, ... %!!4
'Prop2', value, ... %!!18
'Prop3', value); %!!18
% Indent past = sign for assignments.
A = 1 + ... % !!4
2; % !!8
medvar = 1 + ... % !!4
2; % !!13
alongvariablename = 1 +... % !!4
2; % !!8
fancyfunctionname(arg1, ... %!!4
innerfcn(arg2, ... %!!22
arg3), ... %!!31
{ cell1; %!!22
cell2; %!!24
[ 1 2 ; %!!24
3 4 ] ; %!!26
cell 4 },... %!!24
arg5); %!!22
... % Continuation by itself just before an end.
end %!!0
function [ a, ... !!0
b, ... !!11
c, ... !!11
d ] ... !!11
= continuations_in_return(opt)
% H1 Comment Line !!0
code(); %!! 4
end %!! 0
function [ a, b ] = continuation_in_args(a,...
b) %!!41
% H1 comment line !!0
end
function c=expression_cont(a,b)
% H1 line !!0
if (a > 0 && ... %!!4
b < 0) %!!8
% comment one !!8
c=1; %!!8
elseif (a < 0 && ... %!!4
b > 0) %!!12
% comment two !!8
c=2; %!!8
end %!!4
switch a %!!4
case 'ert' %!!6
b = 1; %!!8
case {'sim', ... %!!6
'normalsim'} %!!12
b=2; %!!8
end %!!4
end
function a=odd_if_location(n) %!!0
i=1; while i<10, if xfcn(i,n)==0, i=i+1; %!!4
else, i=20; end; end %!!21
if i<20 %!!4
a=1; %!!8
else %!!4
a=0; %!!8
end %!!4
foo();
end %!!0
function val = localfcn(c,d) %!!0
% !!0 Help Comment
try fclose( fid ); catch, end %!!4
val = c+d; % !!4
odd_end_location_from_dspfwiz_load(1);
end %!!0
function [z,o,n]=odd_end_location_from_dspfwiz_load(opts) %!!0
if opts.zeros, z = 'On'; %!!4
else, z = 'Off'; end %!!4
if opts.ones, o = 'On'; %!!4
else, o = 'Off'; end %!!4
if opts.neg_ones, n = 'On'; %!!4
else, n = 'Off'; end %!!4
end %!!0
function a=foo
%{
for !!2
for !!2
%}
if true %#ok %!!4
if true %!!8
a = 1; %!!12
end end %#ok %!!8
end %!!0

2
tests/empty.m Normal file
View File

@ -0,0 +1,2 @@
% Empty M file.
% %%%empty guess guess

View File

@ -1,8 +1,36 @@
function [a, b, c] = fontlock(input1, ...
input2, ...
input3)
% FONTLOCK testing function for Emacs & MATLAB.
% TEST FILE FOR FONT LOCK SPECIAL WORDS
function fontlock()
%^ ^kw ^fn ^df
%^ ^ig
% $$$ ignored comment
%^ ^ig
persistent var1 % !!4
%^ ^kw ^vn ^co
global var2 % !!4
%^ ^kw ^vn ^co
end
%^ ^kw
function [ var1, var2 ] = local(input1)
%^ ^kw ^vn ^fn ^vn ^df
end
function [a, b] = local2(input1,...
input2)
%^ ^vn
end
% TODO - these are cross function variables, but we turn off mlint in
% tests, so these aren't tested.
function [a, b, c] = localvars(input1, input2, input3)
%^ ^kw ^vn ^fn ^vn ^vn ^vn ^df
nested(input1);
q = input2;
@ -16,6 +44,135 @@ function [a, b, c] = fontlock(input1, ...
c = r;
end
end
function keywordstuff()
while true
%^ ^kw ^ma
for varname=1:10
%^ ^kw ^vn ^cn
break
%^ ^kw
end
if varname == 2
%^ ^kw ^df ^bi ^df
disp(1)
elseif varname==3
%^ ^kw ^df ^bi
disp(2)
else
%^ ^kw
disp(3)
end
%^ ^kw
switch varname
%^ ^kw ^cn
case 1
%^ ^kw ^cn
disp(1)
continue
%^ ^kw
case 2
%^ ^kw ^cn
disp(2)
otherwise
%^ ^kw
disp('other');
%^ ^df ^st
return
%^ ^kw
end
%^ ^kw
try
%^ ^kw
disp(1)
catch
%^ ^kw
disp(2)
end
%^ ^kw
end
end
function dographics(value)
f = figure;
%^ ^bi
ax = axes(f);
%^ ^bi
set ( ax, 'property',value)
%^ ^bi ^vn ^st
s = open_system('foo.mdl');
%^ ^si ^st
set_param(s, 'param', value);
%^ ^si ^vn ^st ^df
end
function dodebug()
dbstop in dodebug
%^ ^bo ^cd
dbclear
%^ ^bo
end
function mathstuff()
myvar = eps + pi + nan + ans + i + NaT + true ;
%^ ^df ^ma ^bi ^ma ^bi ^ma ^bi ^ma ^bi ^ma ^bi ^ma ^bi ^ma ^df
end
function helptest()
% HELPTEXT has fancy fonts in it.
%^ ^cn
end
function name_no_args
%^ ^kw ^fn
end
function retarg = ret_and_name_no_args % comment
%^ ^kw ^vn ^df ^fn ^co
end
function retarg = args_have_cont (arg_1, ...
arg_2)
%^ ^vn
end
function [ retarg1, ...
retarg2, ...
retarg3 ] ...
= name_of_fcn (arg1)
%^ ^df ^fn ^vn ^df
end
classdef (Abstract) myclass < handle
%^ ^kw ^ty ^fn ^bi ^cn
end
classdef (Abstract)myclass<handle
%^ ^kw ^ty ^fn ^bi ^ty
end
%{
% Local Variables:
% matlab-show-mlint-warnings: nil
% End:
%}

View File

@ -1,4 +1,5 @@
function indents(a,b,stuff)
function indents(a,b,stuff,cmddual1fake,cmddual2fake)
% Help text
% !!0
% of many lines
@ -11,26 +12,37 @@ function indents(a,b,stuff)
a (1,1) {mustBeNumeric} % !!8
b (:,:) double % !!8
stuff {mustBeMember(stuff, { 'this' 'that' 'other' })} % !!8
cmddual1fake double % !!8
cmddual2fake int % !!8
end % !!4
persistent var1 % !!4
global var2 % !!4
persistent var3 % !!4
locala = a; %#ok
localb = b; %#ok
localstuff = stuff; %#ok
ends_in_comments_and_strings(); % has end in name
if isempty(var1) var1=1; end %#ok !!4
if isempty(var3) var3=2; end %#ok !!4
ends_in_comments_and_strings(var1, var2, var3); % !!4 has end in name
% !!4
block_starts_in_comments_and_strings();
block_starts_in_comments_and_strings(cmddual1fake,cmddual2fake);
array_constant_decls();
% !!4
continuations_and_block_comments();
% $$$ !!0
% $$$ !!0
% $$$ special ignore comments
has_nested_fcn();
has_nested_fcn(); % !!4
% !!4 - after ignore comments
@ -52,7 +64,8 @@ function B = ends_in_comments_and_strings()
B = A(1:end); %#ok
if foo
%% cell start comment !!4
if foo %!!4
C = "this is the end of the line";
% !!8
else %!!4
@ -62,25 +75,25 @@ function B = ends_in_comments_and_strings()
% !!4
E = [ D C];
if bar
A = E;
end; B = A(1:end);
% !!4
E = B;
if baz
A = C;
end; B = [ 1 2 ... % is this the end?
3 4 ]; % !!15
% !!4
if foo
A = E;
@ -88,21 +101,15 @@ function B = ends_in_comments_and_strings()
end ... the other end
% !! 4
code1() ...
code2(); %!!8
% NOTE: Blank space below should cancel the indent effect of ellipsis.
code1() ...
B = [ B A ]; % !!4
str = 'This is a char array with ... in it';
foo(str); % !!4
fcncall(arg1, '...', arg3); % !!4
1; % !!4
% Multi-ends
% Multi- end s
% >>8
if foo %#ok
if bar %#ok
@ -116,24 +123,126 @@ function B = ends_in_comments_and_strings()
% !!4
B = A;
end
function out = array_constant_decls()
A = [ 1 2 3 ]; %!!4
Blong = [ 1 2; %!!4
3 4; %!!14
]; %!!12
Csep = [
1 2; %!!8
3 4; %!!8
]; %!!11
multinest = { [ 1 2 %!!4
3 4 ]; %!!20
{ 5 6 7 ... %!!18
8 9 10 ... %!!20
}; %!!18
fcncall(10, ... %!!18
12, ... %!!26
[ 13 14; %!!26
15 16 ]) %!!28
} ; %!!16
nest = { ... %!!4
1 %!!8
[ ... %!!8
2 3 %!!10
] ... %!!8
3 %!!8
}; %!!11
cascade_long_name = ... %!!4
{ ... %!!8
1 %!!10
2 %!!10
}; %!!8
% TODO
% I don't know why the below indents this way.
% It should either do all max indent, or all lined up with parens.
thing.thing.long.long.longname({ 'str' %!!4
'str' %!!37
'str' %!!37
'str' %!!37
}); %!!35
thing.thing.long.long.longname('str', ... %!!4
'str', ... %!!35
'str', ... %!!35
'str' ... %!!35
); %!!34
% Line starting with end inside parens
disp(Csep(1: ... %!!4
end)); %!!14
% This array has bad syntactic expression parsing due to the
% apostrophy
Closures = [
755009 ; ... % 21-Feb-2067 Washington's Birthday (Mon)
755010 ; % !!8
];
dep = [
root(info.function, factory, workspace, []), ... % likewise this isn't a keyword
fcn3.finalize % the single quote used to break [] scanning
];
% This long fcn name last symbol starts with 'get' which
% used to confuse and move to indent past 1st arg.
if qesimcheck.utils.GetYesNoAnswer('Do ',... !!4
'n',... !!39
'once') %!!39
code(); %!!8
end %!!4
% !!4
out = { A %!!4
Blong %!!12
Csep %!!12
nest %!!12
multinest%!!12
cascade_long_name%!!12
Closures%!!12
dep %!!12
}; %!!10
end
function C = block_starts_in_comments_and_strings()
function C = block_starts_in_comments_and_strings(varargin)
% !!0
C = 0;
if true % if true
if varargin{1} % if true
% !!8
else % !!4
% !!8
end % if true
% see previous function
% !!4
for x=1:length(C) % !!4
% !!8
if varargin{2} % !!8
continue % !!12
end % !!8
break % !!8
% !!14
%!!8
end
switch foo() %!!4
@ -149,7 +258,7 @@ function C = block_starts_in_comments_and_strings()
try
% !!8
catch %!!4
% !!8
end
@ -160,10 +269,12 @@ function B = continuations_and_block_comments
% !!0
% !!0
%{
!!6
!!6
%}
%{
!!2 { }
!!2
%}
arg1=1;
%{
% !!4
@ -188,9 +299,9 @@ function B = continuations_and_block_comments
3 4 ]; % !!10
foo(['this is a very long string' ... %!!4
'with a continution to do something very exciting'])%!!9
'with a continution to do something very exciting']);%!!9
set(gcf,'Position',[ 1 2 3 4],... !!4
set(gcf,'Position',[ 1 2 3 4], ... !!4
'Color', 'red'); % !!12
B = A + 1 + 4 ...
@ -200,6 +311,14 @@ function B = continuations_and_block_comments
% continuation-comment !!17
% !!4 -blank between this & continuation comment
% !!4 - more comments
if condition1 || ... % !!4
fcn_call(arg1, ... % !!12
arg2) % !!21
line_in_if();
end % !!4
end
@ -208,15 +327,44 @@ function has_nested_fcn
plot(1:10); %!!4
function am_nested_fcn %!!4
A = 1;
function am_nested_fcn() %!!4
% help
% !!4
code();
code(A);
%!!8
end
%!!4
am_nested_fcn();
function_end_same_line(1);
function_after_end_same_line();
end
function b=function_end_same_line(a), b=a; end %!!0
function function_after_end_same_line()%!!0
%!!0
disp('foo');%!!4
debug_cmd_dual();
end%!!0
function debug_cmd_dual ()
% These dbstop command dual content have 'if' blocks in them.
% The command dual detection needs to block these from being
% detected as block initiators which would cause indentaiton.
dbstop in hRandomFile at 14 if func() % !!4
dbstop in hRandomFile at 30@1 if x==1 % !!4
dbstop in hPFile % !!4
dbstop in hSimpleFile at 2 % !!4
dbstop if error % !!4
%!!4
debug_cmd_dual(); %!!4
end

View File

@ -1,30 +1,39 @@
% >>1
classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
%^ ^kw ^ty ^fn ^cn ^bi ^cn ^co
% !!0
% %%% class class class
% >>11
properties (Access='public') % #2#
%^ ^kw ^ty ^st ^co
% !!8
AP = []; % #2#
%^ ^vn ^df ^co
AB = 'charvec with space'; % #2#
AC = "string with space and ( "; % #2#
AD = fun_call(1,2); % #3#
AE (1,:) double {mustBePositive} = 1; % #5#
%^ ^vn ^ty ^df ^co
end % <<11
% >> 111
properties (AbortSet=true, NonCopyable=true) % #2#
%^ ^kw ^ty ^ma ^ty ^ma ^co
% !!8
AF (1,1) char {mustBeMember(AF, {'High','Medium','Low'})} = 'Low'; % #5#
%^ ^vn ^ty ^df ^st ^df ^st ^co
AG (1,1) matlab.lang.OnOffSwitchState = 'on'; % #6#
%^ ^vn ^ty ^ty ^st ^co
end % <<111
% >> 112
events
% !!8
Event1
%^ ^vn
Event2
end % <<112
@ -34,10 +43,14 @@ classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
% >>16
function obj = mclass()
%^ ^kw ^vn ^fn ^df
% !!8
obj.AB = obj.AP(1:end);
% !!12
unusedvar = 1; %#ok
%^ ^df ^pr
disp('charvect with if and for words [ in it'); % #2#
@ -46,18 +59,22 @@ classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
notify(obj,'Event1',...
'indent test');
notify(obj, 'Event1', 'indent test');
%^ ^df ^vn ^st ^st ^df
% >>17
while obj.AB % #3#
disp("while loop going on here ("); % #2#
% !!52
% !!16
end % <<17
error('function mclass in charvec }'); % #2#
% !!12
end % <<16
@ -66,7 +83,7 @@ classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
methods (Access='public')
% >>13
function meth(obj) % #3#
% >>14
if obj.AP % #3#
@ -74,8 +91,8 @@ classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
else
% >>15
try
% >>15
try
% comment with if, while, parfor words in it.
@ -91,13 +108,52 @@ classdef (abstract) mclass < handle & matlab.mixin.SetGetExactNames % #7#
end % <<12
methods (Abstract, Hidden=true) % #2#
result = abs_func(a,b) % #3#
result = other_abs_fun(a,b) % #3#
end
methods %!!4
function end_separate_line(~) %!!8
end %!!8
function end_same_line(~), end %!!8
function after_end_same_line(~), end %!!8
end %!!4
methods %!!4
function properties(~) %!!8
end %!!8
function methods(~) %!!8
end %!!8
function events(~) %!!8
end %!!8
function arguments(~) %!!8
end %!!8
function enumeration(~) %!!8
end %!!8
function usestuff(obj) %!!8
% Try using the methods of this object
obj.properties(); %!!12
%^ ^df ^df ^co
obj.methods(); %!!12
obj.events(); %!!12
obj.arguments(); %!!12
obj.enumeration(); %!!12
end %!!8
end %!!4
end % <<1
% End

8
tests/mclass_cont.m Normal file
View File

@ -0,0 +1,8 @@
classdef mclass_cont < otherThing & ... % !!0 This continuation can case issue for next prop block
handle %!!8
properties (Dependent, SetAccess = private) %!!4
TopicName %!!8
end %!!4
end %!!0

View File

@ -34,6 +34,7 @@
(require 'matlab-load)
(require 'matlab)
(require 'cedet-matlab)
(require 'matlab-complete)
(require 'semantic-matlab)
;; Enable semantic
@ -42,68 +43,224 @@
(defun metest-all-syntax-tests ()
"Run all the syntax tests in this file."
(metest-comment-string-syntax-test)
(metest-sexp-counting-test)
(metest-sexp-traversal-test)
(metest-indents-test)
(metest-parse-test)
(setq debug-on-error t)
(matlab-scan-stat-reset ) ;; Enable scanner statistics logging.
(metest-log-init)
(setq-default matlab-indent-function-body 'guess) ;; Force the guess system to be exercised.
(metest-run 'metest-end-detect-test)
(setq-default matlab-indent-function-body 'MathWorks-Standard) ;; put it back
(metest-run 'metest-comment-string-syntax-test)
(metest-run 'metest-fontlock-test)
(metest-run 'metest-sexp-counting-test)
(metest-run 'metest-sexp-traversal-test)
;; Randomize indentation first before indenting
;; to force the indenter to make changes and give
;; the cahce and performance a harder problem.
(metest-indents-randomize-files)
(metest-run 'metest-indents-test)
;; Parsing and completion are high level tools
(metest-run 'metest-parse-test)
(metest-run 'metest-complete-test)
(metest-log-report (metest-log-write))
(matlab-scan-stats-print)
)
(defun metest-run (test)
"Run and time TEST."
(let* ((config (symbol-value test))
(name (if (stringp config) config (car config)))
(files (or (cdr-safe config) '("")))
(strlen (apply 'max (mapcar 'length files))))
(message ">> Starting %s loop on %S" name files)
(dolist (F files)
(princ (format (concat "<< %s %-" (number-to-string strlen) "s ") name F) 'external-debugging-output)
(let ((old debug-on-error)
(out (progn (setq debug-on-error nil)
(metest-timeit test F))))
(setq debug-on-error old)
(when (listp out)
(princ (format "passed: %s %.2f s\n" (cdr out) (car out)) 'external-debugging-output)
)
))
(message "")))
(defvar metest-test-error nil)
(defmacro metest-condition-case-error-msg (&rest forms)
"Run FORMS, capturing any errors and associating with (point)."
(declare (indent 0) (debug t))
`(condition-case err
,@forms
(error (cond (metest-test-error (error (car (cdr err))))
(t (metest-error "Lisp: %s" (error-message-string err))))
0)
))
(defvar met-end-detect-files '("empty.m" "stringtest.m" "mfuncnoend.m" "mfuncnoendblock.m" "mfuncends.m" "mclass.m" "mfuncspacey.m" "mfuncnoendindent.m" "mfuncnofuncindent.m")
"List of files for running end detection tests on.")
(defvar metest-end-detect-test (cons "END detection" met-end-detect-files))
(defun metest-end-detect-test (F)
"Run a test to make sure we correctly detect the state of managing 'end'."
(let ((buf (metest-find-file F))
(ret nil)
(cnt 0))
(with-current-buffer buf
(goto-char (point-min))
;;(message ">> Checking END detection in %S" (current-buffer))
(if (re-search-forward "%%%\\s-*\\(\\w+\\)\\s-+\\(\\w+\\)\\s-+\\(\\w+\\)$" nil t)
(let ((st-expect (intern (match-string-no-properties 1)))
(end-expect (intern (match-string-no-properties 2)))
(indent-expect (intern (match-string-no-properties 3)))
(st-actual (matlab-guess-script-type))
(end-actual (matlab-do-functions-have-end-p))
(indent-actual (matlab-indent-function-body-p))
)
(unless (eq st-actual st-expect)
(metest-error "Script type detection failure: Expected %s but found %s"
st-expect st-actual))
(unless (eq end-actual end-expect)
(metest-error "Script end detection failure: Expected %s but found %s"
end-expect end-actual))
(unless (eq indent-actual indent-expect)
(metest-error "Script indent detection failure: Expected %s but found %s"
indent-expect indent-actual))
(setq ret (list "script[" st-actual "] end[" end-actual "] indent-p[" indent-actual "]"))
;;(message "<< Script type and end detection passed: %s, %s" st-actual end-actual)
)
;; No expected values found in the file.
(metest-error "Test file did not include expected script-type cookie")
))
ret))
(defvar met-stringtest-files '("stringtest.m")
"List of files for running string tests on.")
(defun metest-comment-string-syntax-test ()
(defvar metest-comment-string-syntax-test (cons "string/comment detection" met-stringtest-files))
(defun metest-comment-string-syntax-test (F)
"Run a test to make sure string nd comment highlighting work."
(dolist (F met-stringtest-files)
(let ((buf (find-file-noselect (expand-file-name F met-testfile-path)))
(cnt 0))
(let ((buf (metest-find-file F))
(cnt 0)
(noninteractive nil) ;; fake out font lock
)
(with-current-buffer buf
(goto-char (point-min))
(message ">> Starting search loop in %S" (current-buffer))
(while (re-search-forward "#\\([csveb]\\)#" nil t)
(goto-char (match-end 1))
(let ((md (match-data))
(mc (match-string 1))
(bc (matlab-ltype-block-comm))
(qd (matlab-cursor-comment-string-context)))
(let ((md (match-data)))
;; Force font lock to throw catchable errors.
(font-lock-mode 1)
(font-lock-flush (point-min) (point-max))
(font-lock-ensure (point-min) (point-max))
(font-lock-fontify-region (point-min) (point-max))
;; FL test 1: make sure font lock is on and match data didn't change.
(unless font-lock-mode
(metest-error "Font Lock failed to turn on."))
;;(unless (equal md (match-data))
;; (metest-error "Font Locking transmuted the match data"))
(when (not (get-text-property 2 'fontified))
(metest-error "Font Lock Failure: can't run test because font lock failed to fontify region."))
)
;;(message ">> Starting string/comment detect loop in %S" (current-buffer))
(while (re-search-forward "#\\([cCisSvVebdr]\\)#" nil t)
(let* ((md (match-data))
(pt (match-end 1))
(mc (match-string-no-properties 1))
(fnt (get-text-property pt 'face))
(lv1 (matlab-compute-line-context 1))
(bc (metest-condition-case-error-msg (matlab-line-block-comment-start lv1)))
(qd (metest-condition-case-error-msg (matlab-cursor-comment-string-context)))
)
(goto-char pt)
;; Test 1 - what are we?
(unless (or (and (string= "b" mc) bc)
(unless (or (and (string= "b" mc) (and bc (eq 'comment qd)))
(and (string= "v" mc) (eq 'charvector qd))
(and (string= "V" mc) (eq 'charvector qd))
(and (string= "s" mc) (eq 'string qd))
(and (string= "S" mc) (eq 'string qd))
(and (string= "c" mc) (eq 'comment qd))
(and (string= "e" mc) (eq 'elipsis qd))
(and (string= "C" mc) (eq 'comment qd))
(and (string= "i" mc) (eq 'comment qd))
(and (string= "e" mc) (eq 'ellipsis qd))
(and (string= "d" mc) (eq 'commanddual qd))
(and (string= "r" mc) (eq nil qd))
)
(error "Syntax Test Failure @ line %d: Expected %s but found %S"
(line-number-at-pos)
(cond ((string= mc "b") "block comment")
((string= mc "v") "charvector")
((string= mc "s") "string")
((string= mc "c") "comment")
((string= mc "e") "elipsis")
(t "unknown test token"))
qd))
(metest-error "Syntax Test Failure @ char %d: Expected %s but found %S"
pt
(cond ((string= mc "b") "block comment")
((string= mc "v") "charvector")
((string= mc "V") "charvector")
((string= mc "s") "string")
((string= mc "S") "string")
((string= mc "c") "comment")
((string= mc "C") "comment")
((string= mc "i") "comment")
((string= mc "e") "ellipsis")
((string= mc "d") "commanddual")
((string= mc "r") "normal code")
(t "unknown test token"))
qd))
;; Test 2 - is match-data unchanged?
(unless (equal md (match-data))
(error "Syntax checking transmuted the match data"))
(metest-error "Syntax checking transmuted the match data"))
;; FL test 2 - Is the matched location fontified correctly?
(when (consp fnt) (setq fnt (car fnt)))
(unless (or (and (string= "b" mc) (eq fnt 'font-lock-comment-face))
(and (string= "v" mc) (eq fnt 'font-lock-string-face))
(and (string= "V" mc) (eq fnt 'matlab-unterminated-string-face))
(and (string= "s" mc) (eq fnt 'font-lock-string-face))
(and (string= "S" mc) (eq fnt 'matlab-unterminated-string-face))
(and (string= "c" mc) (eq fnt 'font-lock-comment-face))
(and (string= "C" mc) (eq fnt 'matlab-cellbreak-face))
(and (string= "i" mc) (eq fnt 'matlab-ignored-comment-face))
(and (string= "e" mc) (eq fnt 'font-lock-comment-face))
(and (string= "d" mc) (eq fnt 'matlab-commanddual-string-face))
(and (string= "r" mc) (eq fnt nil))
)
(metest-error "Font Lock Failure @ char %d: Expected %s but found %S"
pt
(cond ((string= mc "b") "comment face")
((string= mc "v") "string face")
((string= mc "V") "unterminated string face")
((string= mc "s") "string face")
((string= mc "S") "unterminated string face")
((string= mc "c") "comment face")
((string= mc "C") "cellbreak face")
((string= mc "i") "ignored comment face")
((string= mc "e") "comment face")
((string= mc "d") "commanddual string face")
((string= mc "r") "regular code / no face")
(t "unknown test token"))
(get-text-property pt 'face)))
;; Track
(setq cnt (1+ cnt))
))
(kill-buffer buf))
(message "<< Comment and string syntax test: %d points passed" cnt)
))
(message ""))
(list cnt "tests")))
(defvar met-sexptest-files '("expressions.m" "mclass.m")
(defvar met-sexptest-files '("expressions.m" "mclass.m" "blocks.m")
"List of files for running syntactic expression tests.")
(defun metest-sexp-counting-test ()
"Run a test to make sure string nd comment highlighting work."
(dolist (F met-sexptest-files)
(let ((buf (find-file-noselect (expand-file-name F met-testfile-path)))
(defvar metest-sexp-counting-test (cons "sexp counting" met-sexptest-files))
(defun metest-sexp-counting-test (F)
"Run a test to make sure string and comment highlighting work."
(let ((buf (metest-find-file F))
(cnt 0))
(with-current-buffer buf
(goto-char (point-min))
(message ">> Starting sexp counting loop in %S" (current-buffer))
;;(message ">> Starting sexp counting loop in %S" (current-buffer))
(while (re-search-forward "#\\([0-9]\\)#" nil t)
(save-excursion
(goto-char (match-beginning 0))
@ -111,102 +268,122 @@
(let* ((num (string-to-number (match-string 1))))
(save-restriction
(narrow-to-region (point-at-bol) (point))
(matlab-move-simple-sexp-internal (- num))
(metest-condition-case-error-msg
(matlab-move-simple-sexp-internal (- num)))
(skip-chars-backward " \t;.=%")
(if (not (eq (point) (point-min)))
(save-restriction
(widen)
(error "error at %d: Backward Sexp miscount tried %d, point %d, min %d"
(line-number-at-pos)
num (point) (point-at-bol))))
(metest-error "Backward Sexp miscount tried %d, point %d, min %d"
num (point) (point-at-bol))))
(skip-chars-forward " \t;.=%")
(matlab-move-simple-sexp-internal num)
(skip-chars-forward " \t\n;.=%")
(if (not (eq (point) (point-max)))
(save-restriction
(widen)
(error "Error at %d: Forward Sexp miscount tried %d, point %d, dest %d"
(line-number-at-pos)
num (point) (point-at-eol)))))
(metest-error "Forward Sexp miscount tried %d, point %d, dest %d"
num (point) (point-at-eol)))))
))
(end-of-line)
(setq cnt (1+ cnt))))
(kill-buffer buf)
(message "<< Sexp counting syntax test: %d points passed" cnt)
))
(message ""))
(list cnt "tests")))
(defun metest-sexp-traversal-test ()
(defvar metest-sexp-traversal-test (cons "sexp block traversal" met-sexptest-files))
(defun metest-sexp-traversal-test (F)
"Run a test to make sure high level block navigation works."
(dolist (F met-sexptest-files)
(let ((buf (find-file-noselect (expand-file-name F met-testfile-path)))
(let ((buf (metest-find-file F))
(cnt 0))
(with-current-buffer buf
(goto-char (point-min))
(message ">> Starting sexp traversal loop in %S" (current-buffer))
;;(message ">> Starting sexp traversal loop in %S" (current-buffer))
(while (re-search-forward ">>\\([0-9]+\\)" nil t)
(let* ((num (string-to-number (match-string 1)))
(num2 0)
(begin nil))
(skip-chars-forward " \n\t;%")
(setq begin (point))
(matlab-forward-sexp)
(skip-chars-forward " \n\t;%")
(if (not (looking-at "<<\\([0-9]+\\)"))
(error "Error at %d: Failed to find matching test end token for %d"
(line-number-at-pos) num)
(setq num2 (string-to-number (match-string 1)))
(when (/= num num2)
(error "Error at %d: Failed to match correct test token. Start is %d, end is %d"
(line-number-at-pos) num num2)))
(matlab-backward-sexp)
(metest-condition-case-error-msg (matlab--scan-block-forward))
(save-excursion
(skip-chars-forward " \n\t;%")
(if (not (looking-at "<<\\([0-9]+\\)"))
(metest-error "Failed to find matching test end token for %d"
num)
(setq num2 (string-to-number (match-string 1)))
(when (/= num num2)
(metest-error "Failed to match correct test token. Start is %d, end is %d"
num num2))))
(metest-condition-case-error-msg (matlab--scan-block-backward))
(when (/= (point) begin)
(error "Error at %d: Failed to reverse navigate sexp for %d"
(line-number-at-pos) num))
(metest-error "Failed to reverse navigate sexp for %d"
num))
)
(end-of-line)
(setq cnt (1+ cnt))))
(kill-buffer buf)
(message "<< Sexp counting syntax test: %d points passed" cnt)
))
(message ""))
(list cnt "test")))
(defvar met-indents-files '("indents.m" "mclass.m")
(defvar met-indents-files '("indents.m" "continuations.m" "mclass.m" "blocks.m" "mfuncends.m" "mfuncnoendblock.m" "mclass_cont.m" "mfuncnofuncindent.m")
"List of files for running syntactic indentation tests.")
(defun metest-indents-test ()
"Run a test to make sure high level block navigation works."
(dolist (F met-indents-files)
(let ((buf (find-file-noselect (expand-file-name F met-testfile-path)))
(cnt 0))
(with-current-buffer buf
(defun metest-indents-randomize-files ()
"Randomize the indentation in the inents test files."
(interactive)
(message "<< Flattening indentation ...")
(let ((matlab-scan-temporal-cache nil)) ;; disable cache for file load
(dolist (F met-indents-files)
(with-current-buffer (metest-find-file F)
(goto-char (point-min))
;; (indent-region (point-min) (point-max))
(message ">> Starting indents loop in %S" (current-buffer))
(while (re-search-forward "!!\\([0-9]+\\)" nil t)
(let* ((num (string-to-number (match-string 1)))
(calc (matlab-calc-indent))
(begin nil))
(when (not (= num calc))
(error "Error at %d: Indentation found is %d, expected %d"
(line-number-at-pos) calc num))
)
(end-of-line)
(setq cnt (1+ cnt))))
(kill-buffer buf)
(message "<< Indentation syntax test: %d points passed" cnt)
))
(message ""))
(while (not (eobp))
(beginning-of-line)
(if (looking-at "^\\s-*$")
(matlab--change-indentation 0)
(matlab--change-indentation 3)) ;;(random 13)?
(forward-line 1)
)
;; And don't delete - leave it to find for the next test.
;; but we do want to restart the mode and force a re-guess of the file type.
(matlab-mode)
))))
(defvar metest-indents-test (cons "indenting" met-indents-files))
(defvar metest-indent-counts 0)
(defun metest-indents-test (F)
"Run a test to make sure high level block navigation works."
(with-current-buffer (metest-find-file F)
(goto-char (point-min))
(let ((metest-indent-counts 0)
(matlab--change-indentation-override #'metest-indents-test-hook-fcn))
(metest-condition-case-error-msg
(matlab-indent-region (point-min) (point-max) nil t))
(kill-buffer (current-buffer))
(list metest-indent-counts "tests"))))
(defun metest-indents-test-hook-fcn (indent)
"Hook fcn used to capture indents from `indent-region'."
(save-excursion
(beginning-of-line)
(when (re-search-forward "!!\\([0-9]+\\)" (point-at-eol) t)
(let ((num (string-to-number (match-string 1))))
(setq metest-indent-counts (1+ metest-indent-counts))
(when (not (eq num indent))
(metest-error "Indentation computed is %s, expected %s"
indent num))))
;; Now do the indent in case a bad indent will trigger a bug later.
(matlab--change-indentation indent)
))
(defvar met-parser-files '("mpclass.m")
"List of files for running semantic parsing tests.")
(defun metest-parse-test ()
(defvar metest-parse-test (cons "semantic parser" met-parser-files))
(defun metest-parse-test (F)
"Run the semantic parsing test to make sure the parse works."
(dolist (F met-parser-files)
(let ((buf (find-file-noselect (expand-file-name F met-testfile-path)))
(let ((buf (metest-find-file F))
exp act
(cnt 0))
(with-current-buffer buf
@ -217,12 +394,12 @@
;; Do the test
(goto-char (point-min))
(message ">> Starting semantic parser test in %S" (current-buffer))
;;(message ">> Starting semantic parser test in %S" (current-buffer))
(unless (re-search-forward "^%%\\s-*>>\\s-+SEMANTIC TEST" nil t)
(error "Semantic parser test: Failed to find test cookie."))
(metest-error "Semantic parser test: Failed to find test cookie."))
(unless (re-search-forward "^%{[ \t\n]+\\(((\\)" nil t)
(error "Semantic parser test: Failed to find expected values."))
(metest-error "Semantic parser test: Failed to find expected values."))
(goto-char (match-beginning 1))
(setq exp (read (buffer-substring (point)
(save-excursion (re-search-forward "%}" nil t)
@ -232,25 +409,262 @@
;; Compare the two lists ... simply.
(while (and exp act)
(unless (metest-compare-tags (car exp) (car act))
(error "Expected tag %s, found %s" (semantic-format-tag-prototype (car exp))
(semantic-format-tag-prototype (car act))))
(metest-error "Expected tag %s, found %s" (semantic-format-tag-prototype (car exp))
(semantic-format-tag-prototype (car act))))
(setq exp (cdr exp) act (cdr act) cnt (1+ cnt))
)
(when (or exp act)
(error "Found tags and expected tag lists differnet lengths.\nExpected Remains: %S\nActual Remains: %S"
(metest-error "Found tags and expected tag lists differnet lengths.\nExpected Remains: %S\nActual Remains: %S"
exp act))
)
(message ">> Semantic parser test: %d tags matched" cnt))))
(list cnt "tests")))
(defun metest-compare-tags (EXP ACT)
"Return non-nil if EXP tag is similiar to ACT"
(semantic-tag-similar-p EXP ACT :documentation)
)
(defconst met-kw-font-alist '(( "kw" . font-lock-keyword-face )
( "ty" . font-lock-type-face )
( "fn" . font-lock-function-name-face )
( "vn" . font-lock-variable-name-face )
( "vc" . (font-lock-variable-name-face
matlab-cross-function-variable-face) )
( "cn" . font-lock-constant-face )
( "co" . font-lock-comment-face )
( "st" . font-lock-string-face )
( "bi" . font-lock-builtin-face )
( "cb" . matlab-cellbreak-face )
( "ig" . matlab-ignored-comment-face )
( "pr" . matlab-pragma-face )
( "cd" . matlab-commanddual-string-face )
( "us" . matlab-unterminated-string-face )
( "ma" . matlab-math-face )
( "si" . matlab-simulink-keyword-face )
( "bo" . bold )
( "df" . nil )
)
"List of testing keywords and associated faces.")
(defvar met-complete-files '("complete.m")
"List of files for running font completion tests.")
(defvar met-complete-tools '((var . matlab-find-recent-variable)
(fcn . matlab-find-user-functions)
)
"List of tools that generate completions.")
(defvar metest-complete-test (cons "completion" met-complete-files))
(defun metest-complete-test (F)
"Test the completion tools in matlab-complete.el"
(let ((buf (metest-find-file F))
exp act
(cnt 0))
(with-current-buffer buf
(goto-char (point-min))
(while (re-search-forward "%\\s-*@@" nil t)
(setq exp (read (buffer-substring-no-properties (point) (scan-sexps (point) 1))))
;; Move to end of previous line, and try to do a complete
(matlab-with-context-line (matlab-previous-code-line (matlab-compute-line-context 2))
(end-of-line)
(let* ((prefix (buffer-substring-no-properties
(save-excursion (forward-word -1) (point))
(point)))
(sem (matlab-lattr-semantics prefix))
)
;; Did we get the expected semantics of this location?
(when (not (eq sem (car exp)))
(metest-error "Completion Semantic Missmatch: Expected %s but found %s" (car exp) sem))
(let* ((expR (nthcdr 2 exp))
(fcn (assoc (nth 1 exp) met-complete-tools))
(act (funcall (cdr fcn) prefix)))
(when (not (equal act expR))
(metest-error "Completion Missmatch: Expected %S but found %S using function %S"
expR act fcn))
)
))
(setq cnt (1+ cnt))
;; Skip this match, find the next.
(end-of-line)))
(list cnt "tests")))
(defvar met-fontlock-files '("fontlock.m" "mclass.m" "blocks.m")
"List of files for running font lock tests.")
(defvar metest-fontlock-test (cons "font lock" met-fontlock-files))
(defun metest-fontlock-test (F)
"Run the semantic parsing test to make sure the parse works."
(let ((buf (metest-find-file F))
(noninteractive nil) ;; fake out font lock
(cnt 0) (fntcnt 0))
(with-current-buffer buf
(goto-char (point-min))
(let ((md (match-data)))
;; Force font lock to throw catchable errors.
(font-lock-mode 1)
(font-lock-flush (point-min) (point-max))
(font-lock-ensure (point-min) (point-max))
(font-lock-fontify-region (point-min) (point-max))
;; FL test 1: make sure font lock is on and match data didn't change.
(unless font-lock-mode
(metest-error "Font Lock failed to turn on."))
;;(unless (equal md (match-data))
;; (metest-error "Font Locking transmuted the match data"))
(when (not (get-text-property 2 'fontified))
(metest-error "Font Lock Failure: can't run test because font lock failed to fontify region."))
)
;; Lines that start with %^ comments are FL keyword test features.
;; Find the line, then look for every ^ and find it's column and match
;; to previous line's column.
(while (re-search-forward "^\\s-*%\\(?: \\$\\$\\$\\)?\\^" nil t)
(let ((next (point-at-eol))
(prevstart (save-excursion (forward-line -1) (point-at-bol)))
)
(while (re-search-forward "\\^\\(\\w\\w\\)\\>" (point-at-eol) t)
(let* ((col (- (match-beginning 0) (point-at-bol)))
(fk (match-string-no-properties 1))
(pt (+ prevstart col))
(fnt (get-text-property pt 'face))
(fnt1 (if (consp fnt) (car fnt) fnt))
(fnt2 (if (consp fnt) (nth 1 fnt) nil))
(exp (cdr (assoc fk met-kw-font-alist))))
(cond
((consp exp)
(when (not (eq (car exp) fnt1))
(metest-error "Bad font layer 1 found @ col %d: Expected %S but found %S"
col (car exp) fnt1))
(when (not (eq (nth 1 exp) fnt2))
(metest-error "Bad font layer 2 found @ col %d: Expected %S but found %S"
col (nth 1 exp) fnt2)))
(t
(when (not (eq exp fnt1))
(metest-error "Bad font found @ col %d: Expected %S but found %S"
col exp fnt))))
(setq fntcnt (1+ fntcnt))
))
(goto-char next)
(setq cnt (1+ cnt))))
(list cnt "lines with " fntcnt "fonts tested"))))
;;; UTILS
;;
(defun metest-find-file (file)
"Read FILE into a buffer and return it.
Do error checking to provide easier debugging."
(let ((F (expand-file-name file met-testfile-path)))
(unless (file-exists-p F)
(error "Test file %s does not exist in %s" file met-testfile-path))
(find-file-noselect F)))
(defvar metest-error-context-lines 4)
(defun metest-error (&rest args)
"Produce an err with standardized file/line prefix."
(declare (indent 1))
(let* ((lineno (line-number-at-pos))
(fname (file-name-nondirectory (buffer-file-name)))
(pre (format "\n%s:%d: Error: " fname lineno))
(post (apply 'format args))
(prelines (min lineno metest-error-context-lines)))
(message "\n--vv buffer snip: %s vv--" fname)
(save-excursion
(forward-line (- prelines))
(while (> prelines 0)
(message "|%s" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
(forward-line 1)
(setq prelines (1- prelines)))
(message ">%s" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
(forward-line 1)
(while (and (> metest-error-context-lines prelines) (not (eobp)))
(message "|%s" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
(forward-line 1)
(setq prelines (1+ prelines))))
(message "---^^ buffer snip ^^---")
(setq metest-test-error t)
(error (concat pre post))))
;;; Logging prormance data for the tests
;;
(defvar metest-log-file "metest_timing_log.dat"
"File to store timing data to.")
(defvar metest-time-log nil
"Data stored for each run.")
(defun metest-log-init ()
"Init the log file and data variable."
(setq metest-time-log nil)
)
(defun metest-shorten (sym)
"Convert SYM into a column header."
(let ((str (symbol-name sym)))
(substring str 7 -5)))
(defun metest-log-write ()
"Write dta into our log file."
(save-current-buffer
(set-buffer (find-file-noselect metest-log-file))
(let ((LOG (reverse metest-time-log)))
(when (= (point-min) (point-max))
;; Initialize the new buffer
(insert "Time\t")
(insert (mapconcat (lambda (log) (metest-shorten (car log))) LOG "\t")))
;; Insert our measurements
(goto-char (point-max))
(newline)
(insert (format-time-string "\"%Y/%m/%d %H:%M\"\t" (current-time)))
(insert (mapconcat (lambda (log2) (format "%f" (cdr log2))) LOG "\t"))
(save-buffer)
;; Go back and find our baseline and return it.
(goto-char (point-min))
(forward-line 1)
(read (concat "(" (buffer-substring-no-properties (point-at-bol) (point-at-eol)) ")"))
)))
(defun metest-log-report (baseline)
"Report via message what happened during the test suite."
(let ((log (reverse metest-time-log))
(base (cdr baseline)))
(princ "Baseln\tRun\tImprovement\tTest\n")
(while (and log base)
(princ (format "%.4f\t" (car base)))
(princ (format "%.4f\t" (cdr (car log))))
(princ (format "%.4f\t\t" (- (car base) (cdr (car log)))))
(princ (metest-shorten (car (car log))))
(princ "\n")
(setq log (cdr log)
base (cdr base)))
))
(defun metest-timeit (fcn &optional file)
"Time running FCN and save result in LOGFILE.
Use this to track perforamnce improvements during development automatically."
(let* ((start (current-time))
(out (funcall fcn file))
(end (current-time))
(diff (float-time (time-subtract end start))))
(if (eq fcn (car-safe (car-safe metest-time-log)))
;; Same fcn, append our number
(setcdr (car metest-time-log) (+ diff (cdr (car metest-time-log))))
(push (cons fcn diff) metest-time-log))
(cons diff out)))
(provide 'metest)
;;; metest.el ends here

26
tests/mfuncends.m Normal file
View File

@ -0,0 +1,26 @@
function mfuncends( )
% Test function that has ends for each function. !!0
% !!0
% Used with test harness to validate indentation and end detection. !!0
% !!0
% %%%function function function
fcn_call(); %!!4
if condition %!!4
fcn_call(); %!!8
fcn_call ... !!8
(); %!!12
end %!!4
end%!!0 - also no space after end
function a=fcn_call(inp) %!!0
while inp > 0 %!!4
fcn_call(inp-1); %!!8
a = [ 1 2 ... !!8
3 4 ]; %!!14
end %!!4
end %!!0

15
tests/mfuncnoend.m Normal file
View File

@ -0,0 +1,15 @@
function mfuncnoend
% A function file that does not have ends at the end of functions.
%
% %%% function nil nil
fcn_call(1)
function fcn_call(idx)
if idx > 0
fcn_call(ix-1)
end

33
tests/mfuncnoendblock.m Normal file
View File

@ -0,0 +1,33 @@
%{
Add a block comment at the beginning to skip over.
x % !!2
% !! 0
%}
function mfuncnoendblock
% A function file that does not have ends at the end of functions.
% !!0
% %%% function nil nil
fcn_call(1) %!!0
function fcn_call(idx) %!!0
if idx > 0 %!!0
fcn_call(ix-1) %!!4
goo(3);
end %!!0
function c=goo(p3) %!!0
if p3 < 3 %!!0
if p3 < 0 %!!4
c = p3 * -3; %!!8
else %!!4
c = p3 * 3; %!!8
for i=1:10 %!!8
c = c + i; %!!12
end %!!8
end %!!4
else %!!0
c=p3*4; %!!4
end %!!0

15
tests/mfuncnoendindent.m Normal file
View File

@ -0,0 +1,15 @@
function mfuncnoendindent
% A function file that does not have ends at the end of functions.
%
% %%% function nil t
fcn_call(1)
function fcn_call(idx)
if idx > 0
fcn_call(ix-1)
end

32
tests/mfuncnofuncindent.m Normal file
View File

@ -0,0 +1,32 @@
function mfuncnofuncindent( )
% Test function that has ends for each function. !!0
% !!0
% Used with test harness to validate indentation and end detection. !!0
% !!0
% %%%function function nil
fcn_call(); %!!0
if condition %!!0
fcn_call(); %!!4
fcn_call ... !!4
(); %!!8
end %!!0
end%!!0 - also no space after end
function a=fcn_call(inp) %!!0
while inp > 0 %!!0
fcn_call(inp-1); %!!4
a = [ 1 2 ... !!4
3 4 ]; %!!10
end %!!0
end %!!0
%{
% Local Variables:
% matlab-indent-function-body: nil
% End:
%}

11
tests/mfuncspacey.m Normal file
View File

@ -0,0 +1,11 @@
function mfuncspacey( )
% Test function that has ends for each function.
%
% Used with test harness to validate indentation and end detection.
%
% %%%function function function
fcn_call();
end

View File

@ -1,6 +1,8 @@
%% Tests for char vector and string handling.
%
% #c#
%
% %%%script script script
%% Basic strings
@ -13,8 +15,8 @@ stringscalar = "string scalar #s#";
% Comment with 'character vector #c#' in it.
% Comment with "string scalar #c#" in it.
charvi = 'char vector incomplete #v#
stringi = "string scalar incomplete #s#
charvi = 'char vector incomplete #V#
stringi = "string scalar incomplete #S#
% Comment with 'char vector incomplete #c#
% Comment with "string scalar incomplete #c#
@ -27,16 +29,16 @@ stringcv = "string scalar with 'char vec #s#' in it";
chard = 'char vector with '' in it #v#';
stringd = "string scalar with "" in it #s#";
chardi = 'incomplete char vector with '' in it #v#
stringdi = "incomplete string scalar with "" in it #s#
chardi = 'incomplete char vector with '' in it #V#
stringdi = "incomplete string scalar with "" in it #S#
%% Strings with Comments
charvc = 'char vector with % comment char #v#';
stringc = "string scalar with % comment char #s#";
charvci = 'incomplete char vector with % comment char #v#
stringci = "incomplete string scalar with % comment char #s#
charvci = 'incomplete char vector with % comment char #V#
stringci = "incomplete string scalar with % comment char #S#
charvbc = 'char vector with %{ comment char #v# %} ';
stringbc = "string scalar with %{ comment char #s# %} ";
@ -69,19 +71,24 @@ icell_in_strs2_nested = { 'charv innercell " #v# }' "strinc innercel ' #s# }"
%% Elipsis as comment
fun_call(); ... This is a comment after an elipsis #e#
fun_call(); ... 'charvec in elipsis comment #e#'
fun_call(); ... "string in elipsis comment #e#"
fun_call(); ... % comment after an elipsis is still elipsis #e#
fun_call(); ... 'charvec in elipsis comment #e#'
fun_call(); ... "string in elipsis comment #e#"
fun_call(); ... % comment after an elipsis is still elipsis #e#
%% Elipsis and strings and other comments
Ecv = 'string with ... in #v# it';
Es = "string with ... in #s# it";
% Comment with ... in it #c#
eecv = '...'; % string with only ellipsis in it #c#
ees = "..."; % string with only ellipsis in it #c#
x = [ 'foo bar',newline,...
' ''-goo'', ... #v#',newline,...
' ''-bar'', ... #v#',newline];
x = [ 'foo bar', newline, ... #e#
' ''-goo'', ... #v#', newline, ... #e#
' ''-bar'', ... #v#', newline ];
func_call1('function with charvec', ... #e#
'after ellipsis charvec with ellipsis ... #v#');
@ -141,7 +148,7 @@ else
Cs = "not unreachable #s#";
end
%% Block Comments #c#
%% Block Comments #C#
%{
@ -154,4 +161,34 @@ end
not_commented();
%{ just a regular comment #c# %} should_be_comment #c#
% Normal comment #c#
%% Ignored Comments #C#
% $$$ This comment is ignored by indentation engine. #i#
%^ This comment is igored too, used in tests for font lock. #i#
%% Command line dual #C#
% Note: stuff after a symbol<space> treated as string
disp _this is string input to function #d#_
disp _this is also string input to a function #d#_
regularcode; #r#
% Note: Case sensitivity of cmd dual functions
DISP _regular code even though ML would treat as cmd dual #r#_
%{
% Local Variables:
% matlab-syntax-support-command-dual: t
% matlab-show-mlint-warnings: nil
% End:
%}
%% END

28
toolbox/emacsrun.m Normal file
View File

@ -0,0 +1,28 @@
function emacsrun(mfile, varargin)
% Run code from MFILE.
% Assumes MFILE was recently edited, and proactively clears that function.
%
% Command sent by Emacs for save-and-go functionality
% Now figure out if shortFileName is on the path.
[ fullFilePath, shortFileName ] = fileparts(mfile);
onpath = ~isempty(which(shortFileName));
if ~exist(fullFilePath,'file')
error('You must save your file into a location accessible by MATLAB process.');
end
% If not on the path, temporarilly switch to that directory so it and an files it references are
% accessible
if ~onpath
oldpath = pwd;
cd(fullFilePath);
cleanup = onCleanup(@()cd(oldpath));
end
clear(shortFileName);
cmd = [ shortFileName varargin{:} ];
evalin('base',cmd);
end