matlab-mode/0000755000175000017500000000000014625574731013772 5ustar sebastiensebastienmatlab-mode/INSTALL0000664000175000017500000000326214611713120015006 0ustar sebastiensebastienInstallation: ** DEPENDANCIES This matlab-emacs build system now requires the CEDET suite. If you are using Emacs version 23.2 or later, you should have everything you need. For older versions of Emacs, visit: http://cedet.sf.net The build system uses EDE for project management, Makefile creation, and autoload generation. ** BUILD To build: make To build using an alternate version of CEDET: make CEDET_PATH=~/my/cedet/lisp/ It is possible to use matlab.el, and associated programs without this extra package. To do so, install and compile only those lisp files you need. ** CONFIGURE EMACS To install all the Emacs tools for MATLAB, add this to your .emacs file: (add-to-list 'load-path "~/path/to/matlab_el") (require 'matlab-load) ** CUSTOMIZATION: To customize the behavior of MATLAB mode, customize the matlab group: M-x customize-group RET matlab RET Some popular customization options: (setq matlab-indent-function-body t) ; if you want function bodies indented (setq matlab-verify-on-save-flag nil) ; turn off auto-verify on save (defun my-matlab-mode-hook () (setq fill-column 76)) ; where auto-fill should wrap (add-hook 'matlab-mode-hook 'my-matlab-mode-hook) (defun my-matlab-shell-mode-hook () '()) (add-hook 'matlab-shell-mode-hook 'my-matlab-shell-mode-hook) Please read the mode help for matlab-mode for additional configuration options. ** TLC To use this mode without using the full install, put the this file into your load path, and add the following to your .emacs file: (require 'tlc) or (autoload 'tlc-mode "tlc" "tlc Editing Mode" t) (add-to-list 'auto-mode-alist '("\\.tlc$" . tlc-mode)) (setq tlc-indent-function t) matlab-mode/matlab-scan.el0000664000175000017500000014571014611713120016466 0ustar sebastiensebastien;;; matlab-scan.el --- Tools for contextually scanning a MATLAB buffer ;; ;; Copyright (C) 2021 Eric Ludlam ;; ;; Author: ;; ;; 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: ;; ;; Handle all the scanning and computing for MATLAB files. ;; ;; * Regular expressions for finding different kinds of syntax ;; * Systems for detecting what is on a line ;; * Systems for computing indentation (require 'matlab-syntax) ;;; Code: ;;; Keyword and REGEX constants ;; ;; List of our keywords, and tools to look up keywords and find out ;; what they are. (defconst matlab-block-keyword-list '(("end" . end) ("function" . decl) ("classdef" . decl) ("arguments" . args) ("properties" . mcos) ("methods" . mcos) ("events" . mcos) ("enumeration" . mcos) ("if" . ctrl) ("elseif" . mid) ("else" . mid) ("ifelse" . mid) ("for" . ctrl) ("parfor" . ctrl) ("while" . ctrl) ("spmd" . ctrl) ("switch" . ctrl) ("case" . case) ("otherwise" . case) ("try" . ctrl) ("catch" . mid) ("break" . keyword) ("continue" . keyword) ("return" . keyword) ("global" . vardecl) ("persistent" . vardecl) ) "List of keywords that are part of code blocks.") (defconst matlab-keyword-table (let ((ans (matlab-obarray-make 23))) (mapc (lambda (elt) (set (intern (car elt) ans) (cdr elt))) matlab-block-keyword-list) ans) "Keyword table for fast lookups of different keywords and their purpose.") (defun matlab-keyword-p (word) "Non nil if WORD is a keyword. If word is a number, it is a match-string index for the current buffer." (let* ((local-word (if (numberp word) (match-string-no-properties word) word)) (sym (intern-soft local-word matlab-keyword-table))) (and sym (symbol-value sym)))) (defsubst matlab-on-keyword-p () "Return the type of keyword under point, or nil." (when (matlab-valid-keyword-syntax) ;; Not in invalid context, look it up. (matlab-keyword-p (buffer-substring-no-properties (save-excursion (skip-syntax-backward "w_") (point)) (save-excursion (skip-syntax-forward "w_") (point)))))) (defvar matlab-kwt-all nil) (defvar matlab-kwt-decl nil) (defvar matlab-kwt-indent nil) (defvar matlab-kwt-end nil) (defvar matlab-kwt-blocks nil) (defvar matlab-kwt-mcos nil) (defvar matlab-kwt-args nil) (defvar matlab-kwt-vardecl nil) (defvar matlab-kwt-fl-simple nil) (defun matlab-keyword-regex (types) "Find keywords that match TYPES and return optimized regexp. TYPES can also be a single symbol that represents a common list of keyword types. These include: all - any kind of keyword decl - declarations, like class or function indent - any keyword that causes an indent end - the end keyword (that causes dedent) blocks - indent and end keywords fl-simple - simple to highilght w/ font lock Caches some found regexp to retrieve them faster." (cond ((or (eq types nil) (eq types 'all)) (or matlab-kwt-all (setq matlab-kwt-all (matlab--keyword-regex nil)))) ((eq types 'decl) (or matlab-kwt-decl (setq matlab-kwt-decl (matlab--keyword-regex '(decl))))) ((eq types 'indent) (or matlab-kwt-indent (setq matlab-kwt-indent (matlab--keyword-regex '(decl ctrl args mcos))))) ((eq types 'end) (or matlab-kwt-end (setq matlab-kwt-end (matlab--keyword-regex '(end))))) ((eq types 'blocks) (or matlab-kwt-blocks (setq matlab-kwt-blocks (matlab--keyword-regex '(end decl ctrl args mcos))))) ((eq types 'fl-simple) (or matlab-kwt-fl-simple (setq matlab-kwt-fl-simple (matlab--keyword-regex '(end decl ctrl mid case keyword vardecl))))) ((eq types 'mcos) (or matlab-kwt-mcos (setq matlab-kwt-mcos (matlab--keyword-regex '(mcos))))) ((eq types 'args) (or matlab-kwt-args (setq matlab-kwt-args (matlab--keyword-regex '(args))))) ((eq types 'vardecl) (or matlab-kwt-vardecl (setq matlab-kwt-vardecl (matlab--keyword-regex '(vardecl))))) ((symbolp types) (matlab--keyword-regex (list types))) (t (matlab--keyword-regex types)))) (defun matlab--keyword-regex (types) "Find keywords that match TYPES and return an optimized regexp." (let ((lst nil)) (mapc (lambda (C) (when (or (null types) (memq (cdr C) types)) (push (car C) lst))) matlab-block-keyword-list) (regexp-opt lst 'symbols))) ;;; Context Parsing ;; ;; Find some fast ways to identify the context for a given line. ;; Use tricks to derive multiple pieces of information and do lookups ;; as quickly as possible. (defun matlab-compute-line-context (level &rest context) "Compute and return the line context for the current line of MATLAB code. LEVEL indicates how much information to return. LEVEL of 1 is the most primitive / simplest data. LEVEL of 2 is stuff that is derived from previous lines of code. This function caches computed context onto the line it is for, and will return the cache if it finds it." (save-match-data (cond ((= level 1) (let ((ctxt (save-excursion (back-to-indentation) (matlab-scan-cache-get)))) (unless ctxt (setq ctxt (matlab-compute-line-context-lvl-1)) (matlab-scan-cache-put ctxt)) ctxt)) ((= level 2) (apply 'matlab-compute-line-context-lvl-2 context)) (t nil))) ) ;;; LEVEL 1 SCANNER ;; ;; This scanner pulls out context available on the current line. ;; This inludes the nature of the line, and anything sytax-ppss gives is. (defconst mlf-ltype 0) (defconst mlf-stype 1) (defconst mlf-point 2) (defconst mlf-indent 3) (defconst mlf-entity-start 4) (defconst mlf-paren-depth 5) (defconst mlf-paren-inner-char 6) (defconst mlf-paren-inner-col 7) (defconst mlf-paren-inner-point 8) (defconst mlf-paren-outer-char 9) (defconst mlf-paren-outer-point 10) (defconst mlf-paren-delta 11) (defconst mlf-end-comment-type 12) (defconst mlf-end-comment-pt 13) (defun matlab-compute-line-context-lvl-1 () "Compute and return the level1 context for the current line of MATLAB code. Level 1 contexts are things quickly derived from `syntax-ppss' and other simple states. Computes multiple styles of line by checking for multiple types of context in a single call using fastest methods." (save-excursion ;; About use of `syntax-ppss' - This util has a 1 element cache, ;; and can utilize the cache as it parses FORWARD only. Tools ;; like `back-to-indentation' also needs to propertize the buffer ;; since that uses syntax elements to do it's work. To make this ;; fcn faster, we call `syntax-ppss' once on BOL, and then ;; parse-partial-sexp for other locations as a way to boost the ;; speed of calls to `syntax-ppss' that come later. ;; Additional note: `back-to-indentation' used below calls ;; syntax-propertize. Command dual needs to call ;; `syntax-ppss' which it does on bol By setting `syntax-ppss' ;; internal cache on bol, in cases where propertize needs to ;; be called, the cache returns immediatly during the ;; propertize, and needs to do no extra work. (beginning-of-line) (let* ((ppsbol (syntax-ppss (point))) ;; Use the cache (pps (progn (back-to-indentation) ;; Compute by hand - leaving cache alone. (parse-partial-sexp (point-at-bol) (point) nil nil ;; previous state ppsbol))) (ppsend (save-excursion ;; Starting @ indentation, parse forward to eol ;; and see where we are. ;; Compute by hand, leaving cache alone. (parse-partial-sexp (point) (point-at-eol) nil nil ;; Previous state pps))) (ltype 'empty) (stype nil) (pt (point)) (indent (current-indentation)) (start (point)) (paren-depth (nth 0 pps)) (paren-inner-char nil) (paren-inner-col nil) (paren-inner-point nil) (paren-outer-char nil) (paren-outer-point nil) (paren-delta (- (car pps) (car ppsend))) (ec-type nil) (ec-col nil) (cont-from-prev nil) (symval nil) ) ;; This means we are somewhere inside a cell, array, or arg list. ;; Find out the kind of list we are in. ;; Being in a multi-line list is valid for all other states like ;; empty lines, and block comments (when (> (nth 0 pps) 0) (save-excursion (goto-char (car (last (nth 9 pps)))) (setq paren-inner-char (char-after (point)) paren-inner-col (current-column) paren-inner-point (point) paren-outer-point (car (nth 9 pps)) paren-outer-char (char-after paren-outer-point) ))) (cond ;; For comments - We can only ever be inside a block comment, so ;; check for that. ;; 4 is comment flag. 7 is '2' if block comment ((and (nth 4 pps) (eq (nth 7 pps) 2)) (setq ltype 'comment stype (cond ((looking-at "%}\\s-*$") 'block-end) ((looking-at "%") 'block-body-prefix) (t 'block-body)) start (nth 8 pps))) ;; If indentation lands on end of line, this is an empty line ;; so nothing left to do. Keep after block-comment-body check ;; since empty lines in a block comment are valid. ((eolp) nil) ;; Looking at a % means one of the various comment flavors. ((eq (char-after (point)) ?\%) (setq ltype 'comment stype (cond ((looking-at "%{\\s-*$") 'block-start) ((looking-at "%%") 'cell-start) ;; The %^ is used in tests ((looking-at "%\\(?:\\^\\| \\$\\$\\$\\)") 'indent-ignore) (t nil)))) ;; Looking at word constituent. If so, identify if it is one of our ;; special identifiers. ((and (= paren-depth 0) (setq symval (matlab-on-keyword-p))) (cond ;; Special end keyword is in a class all it's own ((eq symval 'end) (setq ltype 'end)) ;; If we found this in our keyword table, then it is a start ;; of a block with a subtype. ((memq symval '(decl args mcos ctrl mid case)) (setq ltype 'block-start stype symval)) ;; Some keywords aren't related to blocks with indentation ;; controls. Those are treated as code, with a type. (t (setq ltype 'code stype symval)) )) ;; Looking at a close paren. ((and (< 0 paren-depth) (looking-at "\\s)")) (setq ltype 'close-paren)) ;; Last stand - drop in 'code' to say - yea, just some code. (t (setq ltype 'code)) ) ;; NEXT - Check context at the end of this line, and determine special ;; stuff about it. ;; When the line ends with a comment. ;; Also tells us about continuations and comment start for lining up tail comments. (let ((csc (nth 8 ppsend))) (when (and (not csc) (eq stype 'block-end)) ;; block comment end lines must end in a comment, so record ;; the beginning of that block comment instead. (setq csc (nth 8 pps)) ) ;; If we have something, record what it is. (when csc (setq ec-col csc ec-type (cond ((= (char-after csc) ?\%) 'comment) ((= (char-after csc) ?\.) 'ellipsis) (t 'commanddual))) )) (list ltype stype pt indent start paren-depth paren-inner-char paren-inner-col paren-inner-point paren-outer-char paren-outer-point paren-delta ec-type ec-col ;;cont-from-prev ) ))) ;;; Accessor Utilities for LEVEL 1 ;; ;; Use these to query a context for a piece of data (defmacro matlab-with-context-line (__context &rest forms) "Save excursion, and move point to the line specified by CONTEXT. Takes a lvl1 or lvl2 context. Returns the value from the last part of forms." (declare (indent 1) (debug (form &rest form))) `(save-excursion ;; The CAR of a LVL2 is a LVL1. If __context is LVL1, then ;; the car-safe will return nil (goto-char (nth mlf-point (if (consp (car ,__context)) (car ,__context) ,__context))) ,@forms)) (defsubst matlab-line-point (lvl1) "Return the point at beginning of indentation for line specified by lvl1." (nth mlf-point lvl1)) (defsubst matlab-line-indentation (lvl1) "Return the indentation of the line specified by LVL1." (nth mlf-indent lvl1)) (defsubst matlab-line-empty-p (lvl1) "Return t if the current line is empty based on LVL1 cache." (eq (car lvl1) 'empty)) ;; Comments (defsubst matlab-line-comment-p (lvl1) "Return t if the current line is a comment." (eq (car lvl1) 'comment)) (defsubst matlab-line-regular-comment-p (lvl1) "Return t if the current line is a comment w/ no attributes." (and (eq (car lvl1) 'comment) (eq (nth 1 lvl1) nil))) (defsubst matlab-line-comment-ignore-p (lvl1) "Return t if the current line is an indentation ignored comment." (and (matlab-line-comment-p lvl1) (eq (nth mlf-stype lvl1) 'indent-ignore))) (defsubst matlab-line-comment-style (lvl1) "Return type type of comment on this line." (and (matlab-line-comment-p lvl1) (nth mlf-stype lvl1))) (defsubst matlab-line-end-comment-column (lvl1) "Return column of comment on line, or nil if no comment. All lines that start with a comment end with a comment." (when (eq (nth mlf-end-comment-type lvl1) 'comment) (save-excursion ;; NOTE: if line is in a block comment, this end pt is ;; really the beginning of the block comment. (goto-char (nth mlf-end-comment-pt lvl1)) (current-column)))) (defsubst matlab-line-end-comment-point (lvl1) "Return star of comment on line, or nil if no comment. All lines that start with a comment end with a comment." (when (eq (nth mlf-end-comment-type lvl1) 'comment) (nth mlf-end-comment-pt lvl1))) (defsubst matlab-line-ellipsis-p (lvl1) "Return if this line ends with a comment." (eq (nth mlf-end-comment-type lvl1) 'ellipsis)) (defsubst matlab-line-commanddual-p (lvl1) "Return if this line ends with command duality string." (eq (nth mlf-end-comment-type lvl1) 'commanddual)) (defsubst matlab-line-block-comment-start (lvl1) "Return the start of the block comment we are in, or nil." (when (and (matlab-line-comment-p lvl1) (memq (nth mlf-stype lvl1) '(block-start block-end block-body block-body-prefix))) (nth mlf-entity-start lvl1))) ;; Code and Declarations (defsubst matlab-line-code-p (lvl1) "Return t if the current line is code." (eq (car lvl1) 'code)) (defsubst matlab-line-boring-code-p (lvl1) "Return t if the current line is code w/ no keyword." (and (eq (car lvl1) 'code) (not (nth 1 lvl1)))) (defsubst matlab-line-block-start-keyword-p (lvl1) "Return t if the current line starts with block keyword." (eq (car lvl1) 'block-start)) (defsubst matlab-line-declaration-p (lvl1) "If the current line is a declaration return non-nil. Declarations are things like function or classdef." (and (matlab-line-block-start-keyword-p lvl1) (eq (nth mlf-stype lvl1) 'decl))) (defsubst matlab-line-end-p (lvl1) "Non nil If the current line starts with an end." (eq (car lvl1) 'end)) (defsubst matlab-line-block-middle-p (lvl1) "Non nil If the current line starts with a middle block keyword. These are keywords like `else' or `catch'." (and (eq (car lvl1) 'block-start) (eq (nth 1 lvl1) 'mid))) (defsubst matlab-line-block-case-p (lvl1) "Non nil If the current line starts with a middle block keyword. These are keywords like `else' or `catch'." (and (eq (car lvl1) 'block-start) (eq (nth 1 lvl1) 'case))) (defun matlab-line-end-of-code (&optional lvl1) "Go to the end of the code on the current line. If there is a comment or ellipsis, go to the beginning of that. If the line starts with a comment return nil, otherwise t." (unless lvl1 (setq lvl1 (matlab-compute-line-context 1))) (goto-char (nth mlf-point lvl1)) (if (or (matlab-line-empty-p lvl1) (matlab-line-comment-p lvl1)) nil ;; Otherwise, look for that code. (if (eq (nth mlf-end-comment-type lvl1) 'comment) (goto-char (nth mlf-end-comment-pt lvl1)) (goto-char (point-at-eol))))) (defun matlab-line-end-of-code-needs-semicolon-p (&optional lvl1) "Return non-nil of this line of code needs a semicolon. Move cursor to where the ; should be inserted. Return nil for empty and comment only lines." (unless lvl1 (setq lvl1 (matlab-compute-line-context 1))) (let ((endpt nil)) (save-excursion (when (and (not (matlab-beginning-of-outer-list)) (matlab-line-boring-code-p lvl1) (matlab-line-end-of-code lvl1)) (skip-syntax-backward " ") (when (and (not (matlab-cursor-in-string-or-comment)) (not (= (preceding-char) ?\;))) (setq endpt (point))) )) (when endpt (goto-char endpt)))) (defun matlab-line-first-word-text (&optional lvl1) "Return text for the specific keyword found under point." (matlab-with-context-line lvl1 (buffer-substring-no-properties (point) (save-excursion (skip-syntax-forward "w_") (point)))) ) ;; Declarations and Names (defvar matlab-fl-opt-whitespace) (defun matlab-line-declaration-name (&optional lvl1) "Return string name of a declaration on the line. For functions, this is the name of the function. For classes, this is the name of the class. Output is a list of the form: ( NAME DECL-TYPE START END) Where NAME is the name, and DECL-TYPE is one of 'function or 'class START and END are buffer locations around the found name. If the current line is not a declaration, return nil." (unless lvl1 (setq lvl1 (matlab-compute-line-context 1))) (when (matlab-line-declaration-p lvl1) (let (type name start end) (matlab-navigation-syntax (matlab-with-context-line lvl1 (cond ;; FUNCTION : can have return arguments that need to be skipped. ((string= (matlab-line-first-word-text lvl1) "function") (forward-word 1) (skip-syntax-forward " ") (forward-comment 10) (when (looking-at (concat "\\(\\[[^]]+]\\|\\w+\\)" matlab-fl-opt-whitespace "=")) (goto-char (match-end 0)) (skip-syntax-forward " ") (forward-comment 10) ) (setq type 'function start (point) end (save-excursion (skip-syntax-forward "w_") (point)))) ;; CLASS : Can have class attributes to be skipped. ((string= (matlab-line-first-word-text lvl1) "classdef") (forward-word 1) (skip-syntax-forward " ") (forward-comment 10) (when (looking-at "([^)]+)") (goto-char (match-end 0)) (skip-syntax-forward " ") (forward-comment 10) ) (setq type 'classdef start (point) end (save-excursion (skip-syntax-forward "w_") (point))))))) (when type (list type (buffer-substring-no-properties start end) start end)) ))) ;; Parenthetical blocks (defsubst matlab-line-close-paren-p (lvl1) "Non nil If the current line starts with closing paren (any type.)" (eq (car lvl1) 'close-paren)) (defsubst matlab-line-paren-depth (lvl1) "The current depth of parens at the start of this line" (nth mlf-paren-depth lvl1)) (defsubst matlab-line-close-paren-inner-char (lvl1) "Return the paren character for the parenthetical expression LVL1 is in." (nth mlf-paren-inner-char lvl1)) (defsubst matlab-line-close-paren-inner-col (lvl1) "Return the paren column for the prenthetical expression LVL1 is in." (nth mlf-paren-inner-col lvl1)) (defsubst matlab-line-close-paren-inner-point (lvl1) "Return the paren column for the prenthetical expression LVL1 is in." (nth mlf-paren-inner-point lvl1)) (defsubst matlab-line-close-paren-outer-char (lvl1) "The paren character for the outermost prenthetical expression LVL1 is in." (nth mlf-paren-outer-char lvl1)) (defsubst matlab-line-close-paren-outer-point (lvl1) "The point the outermost parenthetical expression start is at." (nth mlf-paren-outer-point lvl1)) ;;; LEVEL 2 SCANNER ;; ;; This scanner extracts information that affects the NEXT line of ML code. ;; This inludes things like ellipsis and keyword chains like if/end blocks. ;; ;; Level 2 scanning information cascades from line-to-line, several fields will ;; be blank unless a previous line is also scanned. (defconst mlf-level1 0) (defconst mlf-previous-command-beginning 1) (defconst mlf-previous-nonempty 2) (defconst mlf-previous-code 3) (defconst mlf-previous-block 4) (defconst mlf-previous-fcn 5) (defun matlab-compute-line-context-lvl-2 (&optional lvl1 previous2) "Compute the level 2 context for the current line of MATLAB code. Level 2 context are things that are derived from previous lines of code. Some of that context is derived from the LVL1 context such as paren depth, and some scaning previous lines of code. LVL1 will be computed if not provided. This function will generate a mostly empty structure, and will fill in context from the PREVIOUS2 input as needed. Empty stats will be computed by accessors on an as needed basis. If PREVIOUS 2 is not provided, it will go back 1 line, scan it for lvl1 data and use that. Empty stats are filled in as t. nil means there is no context, or a lvl1 stat block for the line with that meaning. The returned LVL2 structure will fill out to be a chain of all previous LVL2 outputs up to a context break. The chains will be summarized in slots in the returned list for quick access." (when (not lvl1) (setq lvl1 (matlab-compute-line-context-lvl-1))) (save-excursion (let ((prev-lvl1 t) (prev-lvl2 t) (prev-cmd-begin t) (prev-nonempty t) (prev-code1 t) (prev-block1 t) (prev-fcn1 t) (tmp nil) ) ;; copy data from previous2. (if previous2 (progn (setq prev-lvl1 t prev-lvl2 t prev-cmd-begin (nth mlf-previous-command-beginning previous2) prev-nonempty (nth mlf-previous-nonempty previous2) prev-code1 (nth mlf-previous-code previous2) prev-block1 (nth mlf-previous-block previous2) prev-fcn1 (nth mlf-previous-fcn previous2) ) (matlab-scan-stat-inc 'prev2) ) ;; Else - previous LVL1 is the one thing we'll compute since we need to ;; init our trackers. (save-excursion (beginning-of-line) (if (bobp) (setq prev-lvl1 nil) (forward-char -1) (setq prev-lvl1 (matlab-compute-line-context 1)) (matlab-scan-stat-inc 'prev2miss) ))) ;; prev line can be nil if at beginning of buffer. (if (not prev-lvl1) (setq prev-lvl2 nil prev-cmd-begin nil prev-nonempty nil prev-code1 nil prev-block1 nil prev-fcn1 nil ) ;; If we do have a previous lvl1, then we can compute from it. ;; Override parts of our data based on prev-lvl2 which might ;; be one of the things we care about. (when (not (matlab-line-empty-p prev-lvl1)) ;; Our prev line was non empty, so remember. (setq prev-nonempty prev-lvl1) (if (not (memq (car prev-lvl1) '(empty comment))) (progn ;; Our prev line wasn't a comment or empty, so remember. (setq prev-code1 prev-lvl1) (when (and (= (matlab-line-paren-depth prev-lvl1) 0) (not (matlab-line-ellipsis-p prev-lvl1))) ;; Our previous line some code, but was not in an ;; array, nor an ellipse. Thus, reset beginning of cmd ;; to this line we are on. (setq prev-cmd-begin lvl1) ) (when (eq (car prev-lvl1) 'block-start) ;; We have a block start, so remember (setq prev-block1 prev-lvl1) (when (eq (nth mlf-stype prev-lvl1) 'decl) (setq prev-fcn1 prev-lvl1)) )) ;; Do something with comment here ?? ))) (list lvl1 prev-cmd-begin prev-nonempty prev-code1 prev-block1 prev-fcn1 )))) ;;; Refresh this lvl2 ;; (defun matlab-refresh-line-context-lvl2 (lvl2 &optional lvl1) "Refresh the content of this lvl2 context. Assume ONLY the line this lvl2 context belongs to has changed and we don't have any caches in later lines." (matlab-with-context-line lvl2 (when (not lvl1) (setq lvl1 (matlab-compute-line-context 1))) ;; cmd begin can be same as self. Check and replace (when (eq (car lvl2) (nth mlf-previous-command-beginning lvl2)) (setcdr (nthcdr mlf-previous-command-beginning lvl2) lvl1)) ;; Replace self. (setcar lvl2 lvl1) )) ;;; Simple Accessors ;; (defun matlab-get-lvl1-from-lvl2 (lvl2) "Return a LVL1 context. If input LVL2 is a level 2 context, return the lvl1 from it. If the input is a lvl1, then return that. If LVL2 is nil, compute it." (if lvl2 (if (consp (car lvl2)) (car lvl2) lvl2) (matlab-compute-line-context 1))) (defun matlab-previous-nonempty-line (lvl2) "Return lvl1 ctxt for previous non-empty line." (let ((prev (nth mlf-previous-nonempty lvl2)) ) (if (eq prev t) ;; Compute it and stash it. (save-excursion (matlab-scan-stat-inc 'nonemptymiss) (beginning-of-line) (skip-syntax-backward " >") ;; skip spaces, and newlines w/ comment end on it. ;; TODO - this stops on ignore comments. (setq prev (matlab-compute-line-context 1)) (setcar (nthcdr mlf-previous-nonempty lvl2) prev)) ;; else record a cache hit (matlab-scan-stat-inc 'nonempty) ) prev)) (defun matlab-previous-code-line (lvl2) "Return lvl1 ctxt for previous non-empty line." (let ((prev (nth mlf-previous-code lvl2)) ) (if (eq prev t) ;; Compute it and stash it. (save-excursion (matlab-scan-stat-inc 'codemiss) (beginning-of-line) (when (not (bobp)) ;; Skip over all comments and intervening whitespace (forward-comment -100000) ;; If no comments, then also skip over these whitespace chars. (skip-chars-backward " \t\n") (setq prev (matlab-compute-line-context 1)) (setcar (nthcdr mlf-previous-code lvl2) prev))) ;; else record a cache hit (matlab-scan-stat-inc 'code) ) prev)) (defun matlab-previous-command-begin (lvl2) "Return lvl1 ctxt for previous non-empty line." (let ((prev (nth mlf-previous-command-beginning lvl2)) ) (if (eq prev t) ;; Compute it and stash it. (save-excursion (matlab-with-context-line (matlab-previous-code-line lvl2) (matlab-scan-beginning-of-command) (setq prev (matlab-compute-line-context 1)) (setcar (nthcdr mlf-previous-command-beginning lvl2) prev))) ;; else record a cache hit (matlab-scan-stat-inc 'cmdbegin) ) prev)) ;;; Scanning Accessor utilities ;; ;; Some utilities require some level of buffer scanning to get the answer. ;; Keep those separate so they can depend on the earlier decls. (defun matlab-scan-comment-help-p (ctxt &optional pt) "Return declaration column if the current line is part of a help comment. Use the context CTXT as a lvl1 or lvl2 context to compute. Declarations are things like functions and classdefs. Indentation a help comment depends on the column of the declaration. Optional PT, if non-nil, means return the point instead of column" (let ((lvl2 nil) (lvl1 nil)) (if (symbolp (car ctxt)) (setq lvl1 ctxt) (setq lvl1 (matlab-get-lvl1-from-lvl2 ctxt) lvl2 ctxt)) (when (matlab-line-comment-p lvl1) ;; Try to get from lvl2 context (let ((c-lvl1 (when lvl2 (matlab-previous-code-line lvl2))) (boc-lvl1 nil)) (save-excursion (unless c-lvl1 ;; If not, compute it ourselves. (save-excursion (beginning-of-line) (forward-comment -100000) (setq c-lvl1 (matlab-compute-line-context 1)))) ;; On previous line, move to beginning of that command. (matlab-scan-beginning-of-command c-lvl1 'code-only) (setq boc-lvl1 (matlab-compute-line-context 1)) ;; On previous code line - was it a declaration? (when (matlab-line-declaration-p boc-lvl1) (matlab-with-context-line boc-lvl1 (if pt (point) (current-indentation))))))))) (defun matlab-scan-previous-line-ellipsis-p () "Return the position of the previous line's continuation if there is one. This is true iff the previous line has an ellipsis." (save-excursion (beginning-of-line) (when (not (bobp)) (forward-char -1) ;; Ellipsis scan always resets at BOL, so call non-cached ;; `parse-partial-sexp' instead of regular `syntax-ppss' which ;; can be slow as it attempts to get a solid start from someplace ;; potentially far away. (let* ((pps (parse-partial-sexp (point-at-bol) (point-at-eol))) ;;(syntax-ppss (point))) (csc (nth 8 pps))) ;; Ellipsis start has a syntax of 11 (comment-start). ;; Other comments have high-bit flags, so don't == 11. (when (and csc (= (car (syntax-after csc)) 11)) csc))))) (defun matlab-scan-beginning-of-command (&optional lvl1 code-only) "Return point in buffer at the beginning of this command. This function walks up any preceeding comments, enclosing parens, and skips backward over lines that include ellipsis. If optional CODE-ONLY is specified, it doesn't try to scan over preceeding comments." (unless lvl1 (setq lvl1 (matlab-compute-line-context 1))) ;; If we are in a block comment, just jump to the beginning, and ;; that's it. (let ((bcs nil)) (goto-char (matlab-line-point lvl1)) (when (not code-only) ;; Code only branch skips comments so the help-comment ;; routine can also use this function. (setq bcs (matlab-line-block-comment-start lvl1)) (when bcs (goto-char bcs) (setq lvl1 (matlab-compute-line-context 1))) ;; If we are in a help comment, jump over that first. (setq bcs (matlab-scan-comment-help-p lvl1 'point)) (when bcs (goto-char bcs) (setq lvl1 (matlab-compute-line-context 1)))) ;; Now scan backward till we find the beginning. (let ((found nil)) (while (not found) ;; first - just jump to our outermost point. (goto-char (or (matlab-line-close-paren-outer-point lvl1) (point))) ;; Second - is there an ellipsis on prev line? (let ((prev (matlab-scan-previous-line-ellipsis-p))) (if (not prev) (setq found t) ;; Move to prev location if not found. (goto-char prev) (setq lvl1 (matlab-compute-line-context 1)) ))) (back-to-indentation) (point)))) (defun matlab-scan-end-of-command (&optional lvl1) "Return point in buffer at the end of this command. This function walks down past continuations and open arrays." (unless lvl1 (setq lvl1 (matlab-compute-line-context 1))) ;; If we are in a block comment, just jump to the end, and ;; that's it. (let ((bcs (matlab-line-block-comment-start lvl1))) (if bcs (progn (goto-char bcs) (forward-comment 1) (setq lvl1 (matlab-compute-line-context 1))) (let ((done nil) (lvlwalk lvl1)) (while (not done) (end-of-line) (cond ((matlab-end-of-outer-list) ;; If we are in a list, this moves to the end and ;; returns non-nil. We are now where we want to be ;; so nothing to do. nil) ((matlab-line-ellipsis-p lvlwalk) ;; This is a continuation, keep going. (forward-line 1)) (t ;; None of these conditions were true, so ;; we must be done! (setq done t)) ) ;; Protect against travelling too far. (when (eobp) (setq done t)) ;; Scan the next line. (when (not done) (setq lvlwalk (matlab-compute-line-context 1))) )) ;; Return where we ended up (end-of-line) (point-at-eol)))) ;;; BLOCK SCANNING and SEARCHING ;; ;; Focused scanning across block structures, like if / else / end. (defvar matlab-functions-have-end) (defvar matlab-indent-level) (defsubst matlab--mk-keyword-node () "Like `matlab-on-keyword-p', but returns a node for block scanning. The elements of the return node are: 0 - type of keyword, like ctrl or decl 1 - text of the keyword 2 - buffer pos for start of keyword 3 - buffer pos for end of keyword" ;; Don't check the context - assume our callers have vetted this ;; point. This is b/c the search fcns already skip comments and ;; strings for efficiency. (let* ((start (save-excursion (skip-syntax-backward "w_") (point))) (end (save-excursion (skip-syntax-forward "w_") (point))) (txt (buffer-substring-no-properties start end)) (type (matlab-keyword-p txt))) (when type (list type txt start end) ))) ;;; Valid keyword locations ;; (defsubst matlab--valid-keyword-point () "Return non-nil if point is valid for keyword. Returns nil for failing `matlab-valid-keyword-syntax'. Returns nil if preceeding non-whitespace char is `.'" (and (matlab-valid-keyword-syntax) (not (matlab-syntax-keyword-as-variable-p)))) (defsubst matlab--known-parent-block(parent) "Return PARENT if it is a known parent block." (if (or (not parent) (eq (car parent) 'unknown)) nil parent)) (defun matlab--valid-keyword-node (&optional node parentblock) "Return non-nil if NODE is in a valid location. Optional parentblock specifies containing parent block if it is known." (when (not node) (setq node (matlab--mk-keyword-node))) (when node (save-excursion (goto-char (nth 2 node)) (and (matlab--valid-keyword-point) (cond ((eq (car node) 'mcos) (matlab--valid-mcos-keyword-point parentblock)) ((eq (car node) 'args) (matlab--valid-arguments-keyword-point parentblock)) (t t)))))) (defun matlab--valid-mcos-keyword-point (&optional parentblock) "Return non-nil if at a location that is valid for MCOS keywords. This means that the parent block is a classdef. Optional input PARENTBLOCK is a precomputed keyword node representing the current block context point is in. Assume basic keyword checks have already been done." (and ;; Must be first thing on line - before checking other stuff. (save-excursion (skip-syntax-backward "w") (skip-syntax-backward " ") (bolp)) ;; If a parent was provided, use that. (if (matlab--known-parent-block parentblock) (string= (nth 1 parentblock) "classdef") ;; else more expensive check (and (eq matlab-functions-have-end 'class) ;; not a class, no mcos allowed. ;; Otherwise, roll back a single command. in MUST be ;; an END indent 4 or CLASSDEF (save-excursion (skip-syntax-backward "w") (forward-comment -100000) (matlab-scan-beginning-of-command) (let ((prev (matlab--mk-keyword-node))) (or (string= (nth 1 prev) "classdef") (and (string= (nth 1 prev) "end") (= (current-indentation) matlab-indent-level))))))))) (defun matlab--valid-arguments-keyword-point (&optional parentblock) "Return non-nil if at a location that is valid for ARGUMENTS keyword. This means that the parent block is a function, and this is first cmd in the function. Optional input PARENTBLOCK is a precomputed keyword node representing the current block context point is in. Assume basic keyword checks have already been done." (save-excursion (skip-syntax-backward "w") (and ;; Must be first thing on line - before checking other stuff. (save-excursion (skip-syntax-backward "w") (skip-syntax-backward " ") (bolp)) ;; More expensive checks (let ((parent (or (matlab--known-parent-block parentblock) ;; technically this can lie, but it's fast. (save-excursion (forward-comment -100000) (matlab-scan-beginning-of-command) (and (matlab--valid-keyword-point) (matlab--mk-keyword-node)))))) (and parent (or (string= (nth 1 parent) "function") ;; If not a function, it might be an and, but that end will need to be ;; reverse tracked to see if it belongs to valid argument block. (and (string= (nth 1 parent) "end") (save-excursion (goto-char (nth 2 parent)) (matlab--scan-block-backward) (let ((prevblock (matlab--mk-keyword-node))) (string= (nth 1 prevblock) "arguments"))) ) )) )))) (defun matlab--scan-derive-block-state (providedstate filter) "Return a block state for current point. If PROVIDEDSTATE is non nil, use that. Return nil if no valid block under pt." (or providedstate (let ((thiskeyword (matlab--mk-keyword-node))) (if (or (not thiskeyword) (not (matlab--valid-keyword-point)) (not (memq (car thiskeyword) filter)) ) nil (push thiskeyword providedstate))))) ;;; Keyword Scanning ;; ;; Use for font locking (defun matlab--scan-next-keyword (keyword-type limit) "Scan for the next keyword of KEYWORD-TYPE and stop. Sets match data to be around the keyword. If nothing is found before LIMIT, then stop and return nil." (let* ((regex (matlab-keyword-regex keyword-type)) (filter (cond ((eq keyword-type 'mcos) #'matlab--valid-mcos-keyword-point) ((eq keyword-type 'args) #'matlab--valid-arguments-keyword-point) (t ;; Not sure - do the super check #'matlab--valid-keyword-node)))) (matlab-re-search-keyword-forward regex limit t filter))) ;;; Block Scanning ;; (defun matlab--scan-block-forward (&optional bounds state) "Scan forward over 1 MATLAB block construct. Return current state on exit. nil - success non-nil - indicates incomplete scanning Also skips over all nexted block constructs along the way. Assumes cursor is in a valid starting state, otherwise ERROR. If cursor is on a middle-block construct like else, case, ERROR. Optional BOUNDS is a point in the buffer past which we won't scan. Optional STATE is the current parsing state to start from. Use STATE to stop/start block scanning partway through." (let ((blockstate (matlab--scan-derive-block-state state '(decl args mcos ctrl))) (thiskeyword nil) (stop nil) (regex (matlab-keyword-regex 'blocks)) ) (when (not blockstate) (error "Not on valid block start.")) (when (not state) (skip-syntax-forward "w")) ;; skip keyword (while (and blockstate (not stop)) (if (not (setq thiskeyword (matlab-re-search-keyword-forward regex bounds t))) (progn (setq stop t) (when (and (not matlab-functions-have-end) (eq (car (car blockstate)) 'decl)) (goto-char (point-max)) (pop blockstate))) (cond ((eq (car thiskeyword) 'end) ;; On end, pop last start we pushed (pop blockstate)) ((eq (car thiskeyword) 'mcos) (if (matlab--valid-mcos-keyword-point (car blockstate)) (push thiskeyword blockstate) ;; else, just skip it )) ((eq (car thiskeyword) 'args) (if (matlab--valid-arguments-keyword-point (car blockstate)) (push thiskeyword blockstate) ;; else, just skip it, not a keyword )) ((and (not matlab-functions-have-end) (eq (car thiskeyword) 'decl) (eq (car (car blockstate)) 'decl) ) ;; No ends on functions - in this case we need treat a function as an end. ;; b/c you can't have nested functions, but only if the thing we try to match ;; it to is another fcn. ;; This POP should result in empty state. (pop blockstate) (goto-char (match-beginning 1))) (t (push thiskeyword blockstate))) )) blockstate)) (defun matlab--scan-block-forward-up (&optional bounds) "Like `matlab--scan-block-forward', but cursor is not on a keyword. Instead, travel to end as if on keyword." (let ((currentstate '((unknown "" 0)))) (matlab--scan-block-forward bounds currentstate))) (defun matlab--scan-block-backward (&optional bounds state) "Scan forward over 1 MATLAB block construct. Return current state on exit. nil - success non-nil - indicates incomplete scanning Also skips over all nexted block constructs along the way. Assumes cursor is in a valid starting state, otherwise ERROR. If cursor is on a middle-block construct like else, case, ERROR. Optional BOUNDS is a point in the buffer past which we won't scan. Optional STATE is the current parsing state to start from. Use STATE to stop/start block scanning partway through." (let ((blockstate (matlab--scan-derive-block-state state '(end))) (thiskeyword nil) (stop nil) (regex (matlab-keyword-regex 'blocks)) ) (when (not blockstate) (error "Not on valid block end.")) (when (not state) (skip-syntax-backward "w")) ;; skip keyword (while (and blockstate (not stop)) (if (not (setq thiskeyword (matlab-re-search-keyword-backward regex bounds t))) (setq stop t) (cond ((eq (car thiskeyword) 'end) ;; On end, push this keyword (push thiskeyword blockstate)) ((eq (car thiskeyword) 'mcos) (when (matlab--valid-mcos-keyword-point nil) (pop blockstate) )) ((eq (car thiskeyword) 'args) (when (matlab--valid-arguments-keyword-point nil) (pop blockstate) )) (t (pop blockstate))) )) blockstate)) (defun matlab--scan-block-backward-up (&optional bounds) "Like `matlab--scan-block-forward', but cursor is not on a keyword. Instead, travel to end as if on keyword." (let ((currentstate '((end "end" 0)))) (matlab--scan-block-backward bounds currentstate))) (defun matlab--scan-block-backward-up-until (types &optional bounds) "Call `matlab--scan-block-backward-up' until we find a keyword of TYPES. Return a keyword node when a matching node is found. Limit search to within BOUNDS. If keyword not found, return nil." (when (symbolp types) (setq types (list types))) (let ((node nil) (done nil) (start (point))) (while (and (not done) (or (not node) (not (memq (car node) types)))) (if (matlab--scan-block-backward-up bounds) (setq done t) (setq node (matlab--mk-keyword-node)))) (if (not done) node (goto-char start) nil))) ;;; Searching for keywords ;; ;; These utilities will simplify searching for code bits by skipping ;; anything in a comment or string. (defun matlab-re-search-keyword-forward (regexp &optional bound noerror bonustest) "Like `re-search-forward' but will not match content in strings or comments. If BONUSTEST is a function, use it to test each match if it is valid. If not then skip and keep searching." (let ((ans nil) (case-fold-search nil) (err nil)) (save-excursion (while (and (not ans) (not err) (or (not bound) (< (point) bound)) (setq ans (re-search-forward regexp bound noerror))) ;; Check for simple cases that are invalid for keywords ;; for strings, comments, and lists, skip to the end of them ;; to not waste time searching for keywords inside. (cond ((matlab-end-of-string-or-comment) ;; Test is only IF in a comment. Also skip other comments ;; once we know we were in a comment. (matlab-end-of-string-or-comment t) (setq ans nil)) ((matlab-in-list-p) (condition-case nil ;; Protect against unterminated lists. (matlab-end-of-outer-list) ;; if no longer in a list, say we're done, move to end ;; of the buffer. (error (goto-char (point-max)) (setq err t))) (setq ans nil)) ((matlab-syntax-keyword-as-variable-p) (setq ans nil)) ((and bonustest (save-match-data (not (funcall bonustest)))) (setq ans nil)) ))) (when ans (goto-char ans) (matlab--mk-keyword-node)))) (defun matlab-re-search-keyword-backward (regexp &optional bound noerror) "Like `re-search-backward' but will not match content in strings or comments." (let ((ans nil) (case-fold-search nil)) (save-excursion (while (and (not ans) (or (not bound) (> (point) bound)) (setq ans (re-search-backward regexp bound noerror))) ;; Check for simple cases that are invalid for keywords ;; for strings, comments, and lists, skip to the end of them ;; to not waste time searching for keywords inside. (cond ((matlab-beginning-of-string-or-comment) (matlab-beginning-of-string-or-comment t) (setq ans nil)) ((matlab-beginning-of-outer-list) (setq ans nil)) ((matlab-syntax-keyword-as-variable-p) (setq ans nil)) ))) (when ans (goto-char ans) (matlab--mk-keyword-node)))) ;;; Quick Queries ;; (defun matlab-scan-block-start-context () "Return a context for the block start matching block point is in. assumes pt is NOT on an end. List contains: 0 - type of keyword the end matched. 1 - column the keyword is on. 2 - lvl1 context of line the keyword is on 3 - lvl1 context of line at beginning of cmnd found keyword is in. Items 2 and 3 are likely the same but could be different." (save-excursion (matlab--scan-block-backward-up) (let* ((keyword (matlab-on-keyword-p)) (column (current-column)) (lvl1-match (matlab-compute-line-context 1)) (lvl1-bgn (save-excursion (matlab-scan-beginning-of-command lvl1-match) (matlab-compute-line-context 1)))) (list keyword column lvl1-match lvl1-bgn)))) ;;; Caching ;; (defvar matlab-scan-temporal-cache nil "Cache of recently computed line contexts. Used to speed up repeated queries on the same set of lines.") (make-variable-buffer-local 'matlab-scan-temporal-cache) (defvar matlab-scan-cache-max 10 "Largest size of the cache. Larger means less computation, but more time scanning. Since the list isn't sorted, not optimizations possible.") (defun matlab-scan-cache-get () "Get a cached context." (let ((pt (point)) (cache matlab-scan-temporal-cache)) (while (and cache (/= pt (nth mlf-point (car cache)))) (setq cache (cdr cache))) ;; If we found a match, return it. (if (and cache (= pt (nth mlf-point (car cache)))) (progn (matlab-scan-stat-inc 'lvl1) (car cache)) (matlab-scan-stat-inc 'lvl1-miss) nil))) (defun matlab-scan-cache-put (ctxt) "Put a context onto the cache. Make sure the cache doesn't exceed max size." (push ctxt matlab-scan-temporal-cache) (setcdr (or (nthcdr matlab-scan-cache-max matlab-scan-temporal-cache) (cons nil nil)) nil)) (defun matlab-scan-before-change-fcn (start end &optional length) "Function run in after change hooks." ;;(setq matlab-scan-temporal-cache nil)) (let ((pt (point)) (cache matlab-scan-temporal-cache) (newcache nil)) ;; Flush whole lines. (save-excursion (goto-char start) (setq start (point-at-bol))) ;; Only drop items AFTER the start of our region. (while cache (if (<= start (matlab-line-point (car cache))) (matlab-scan-stat-inc 'flushskip) (push (car cache) newcache) (matlab-scan-stat-inc 'flush)) (setq cache (cdr cache))) ;;(setq newcache nil) ;;(when (and newcache (symbolp (car newcache))) (debug)) (setq matlab-scan-temporal-cache newcache) )) (defun matlab-scan-setup () "Setup use of the indent cache for the current buffer." (interactive) (add-hook 'before-change-functions 'matlab-scan-before-change-fcn t) (setq matlab-scan-temporal-cache nil)) (defun matlab-scan-disable () "Setup use of the indent cache for the current buffer." (interactive) (remove-hook 'before-change-functions 'matlab-scan-before-change-fcn t) (setq matlab-scan-temporal-cache nil)) ;;; Debugging and Querying ;; (defvar matlab-scan-cache-stats nil "Cache stats for tracking effectiveness of the cache.") (defun matlab-scan-stat-reset (&optional arg) "Reset the stats cache. With no arg, disable gathering stats. With arg, enable gathering stats, and flush old stats." (interactive "P") (if arg (progn (setq matlab-scan-cache-stats nil) (message "Disable matlab scanner stats gathering.")) (message "Emable matlab scanner stats gathering.") (setq matlab-scan-cache-stats (matlab-obarray-make 13)))) (defun matlab-scan-stat-inc (thing) "Increment the stat associated with thing." (when matlab-scan-cache-stats (let ((sym (intern-soft (symbol-name thing) matlab-scan-cache-stats))) (when (not sym) (set (setq sym (intern (symbol-name thing) matlab-scan-cache-stats)) 0)) (set sym (1+ (symbol-value sym)))) ;;(matlab-scan-stats-print 'summary) ) ) (defun matlab-scan-stats-print (&optional summary) "Display stats for scanner hits." (interactive "P") (let ((res nil)) (mapatoms (lambda (sym) (push (cons (symbol-name sym) (symbol-value sym)) res)) matlab-scan-cache-stats) (setq res (sort res (lambda (a b) (string< (car a) (car b))))) (if summary (when (not noninteractive) ;; show a short form. (message (mapconcat (lambda (pair) (concat (car pair) ":" (format "%d" (cdr pair)))) res " "))) ;; Else, show a long form. (let ((printfcn (lambda (pair) (princ (concat (format "%-8s" (concat (car pair) ":")) "\t" (format "%d" (cdr pair)) "\n"))))) (if noninteractive (progn ;; Print to stdout when in batch mode. (princ "\nCache Key\tHits\n") (mapc printfcn res) (princ "---\n")) ;; Display in a buffer (with-output-to-temp-buffer "*MATLAB SCANNER STATS*" (princ "Cache Key\tHits\n----------\t------\n") (mapc printfcn res))) )))) (defun matlab-describe-line-indent-context (&optional lvl1 nodisp) "Describe the indentation context for the current line. If optional LVL1 is specified, describe that instead of computing. If optional NODISP, then don't display, just return the msg." (interactive) (back-to-indentation) (let* ((MSG1 "") (lvl1 (or lvl1 (matlab-compute-line-context 1))) ;(lvl2 (matlab-compute-line-context 2)) ) (let* ((paren-inner-char (nth mlf-paren-inner-char lvl1)) (open (format "%c" (or paren-inner-char ?\())) (close (format "%c" (cond ((not paren-inner-char) ?\)) ((= paren-inner-char ?\() ?\)) ((= paren-inner-char ?\[) ?\]) ((= paren-inner-char ?\{) ?\}) (t ??)))) (innerparenstr (format "%s%d%s" open (nth mlf-paren-depth lvl1) close)) (outerp-char (nth mlf-paren-outer-char lvl1)) (outerp-open (if outerp-char (format "%c" outerp-char) "")) (outerp-close (if (not outerp-char) "" (format "%c" (cond ((= outerp-char ?\() ?\)) ((= outerp-char ?\[) ?\]) ((= outerp-char ?\{) ?\}) (t ??))))) (outerparenopen "") (outerparenclose "") (extraopen "") (extraclose "") ) (cond ((= (nth mlf-paren-depth lvl1) 0) ;; 0 means no parens - so shade out parens to indicate. (setq open (propertize open 'face 'shadow) close (propertize close 'face 'shadow))) ((<= (nth mlf-paren-depth lvl1) 1) ;; If 1 or fewer parens, clear out outer chars (setq outerp-open "" outerp-close "")) ((> (nth mlf-paren-depth lvl1) 2) ;; If more than 2, signal more unknown parens in between (setq outerp-open (concat outerp-open (string (decode-char 'ucs #x2026))) outerp-close (concat (string (decode-char 'ucs #x2026)) outerp-close)))) (if (< (nth mlf-paren-delta lvl1) 0) (setq extraopen (format "<%d" (abs (nth mlf-paren-delta lvl1)))) (when (> (nth mlf-paren-delta lvl1) 0) (setq extraclose (format "%d>" (nth mlf-paren-delta lvl1))))) (setq MSG1 (format "%s%s >>%d %s%s%s%s%s %s %s" (nth mlf-ltype lvl1) (format " %s" (or (nth mlf-stype lvl1) "")) (nth mlf-indent lvl1) ;; paren system extraopen outerp-open innerparenstr outerp-close extraclose (cond ((eq (nth mlf-end-comment-type lvl1) 'comment) "%") ((eq (nth mlf-end-comment-type lvl1) 'ellipsis) "...") ((eq (nth mlf-end-comment-type lvl1) 'commanddual) "-command dual") (t "")) (if (matlab-line-end-comment-column lvl1) (format " %d" (matlab-line-end-comment-column lvl1)) "") )) ) ;; (let* ((lvl2 (matlab-compute-line-context-lvl-2 lvl1)) ;; ;;(comment ;; ) (if nodisp MSG1 (message "%s" MSG1)) )) (provide 'matlab-scan) ;;; matlab-indent.el ends here matlab-mode/.hgignore0000664000175000017500000000046314611713120015560 0ustar sebastiensebastiensyntax: glob *.aux *.bbl *.bib *.blg *.docx *.dvi *.elc *.evc *.glo *.idx *.ilg *.ind *.log *.nav *.nlo *.nls *.ods *.odt *.out *.patch *.snm *.synctex.gz *.tdo *.toc *.vrb *.xls *.xlsx copyright.org toolbox/emacsrunregion_mtl2.m auto/** RCS-old/** ltxpng/** ltximg/** *~ syntax: regexp (.*/)?\#[^/]*\#$ matlab-mode/bin/0000775000175000017500000000000014611713120014522 5ustar sebastiensebastienmatlab-mode/bin/matlab-emacsclient.sh0000775000175000017500000000210114611713120020600 0ustar sebastiensebastien#!/bin/sh # File: matlab-emacs/bin/matlab-emacsclient.sh # Abstract: # Use to redirect MATLAB 'edit foo.m' command such that foo.m opens in Emacs. # # When running MATLAB on Mac, openFileOnMac() of edit.m is passed the applicationName which must be # a single command without switches: # [status, result] = unix(['which ' appInQuotes ]); # This file is the applicationName. # To enable # >> edit foo.m # to open foo.m in Emacs via emacsclient, we need to use arguments to emacsclient to open foo.m. # Thus, this is a wrapper that bridges the gap. This is used on Linux and Mac. Also, to avoid # running an xterm (which doesn't work when ssh'ing into a Mac), we need # checkMacApp(applicationName, 'emacs') of edit.m to return false, therefore cannot use # emacsclient.sh and therefore use matlab-emacsclient.sh if [ "$EDITOR_EMACSCLIENT" = "" ]; then # This should be setup by matlab-emacs/toolbox/+emacs/set.m echo "assert - EDITOR_EMACSCLIENT is not set" exit 1 fi exec $EDITOR_EMACSCLIENT "$@" # LocalWords: emacsclient ssh'ing EMACSCLIENT matlab-mode/matlab-publish.el0000664000175000017500000002044014611713120017200 0ustar sebastiensebastien;;; matlab-publish.el --- Utilities for editing MATLAB files for publishing ;; Copyright (C) 2009, 2019 Uwe Brauer ;; Author: Uwe Brauer oub@mat.ucm.es ;; Maintainer: Uwe Brauer oub@mat.ucm.es ;; Created: 25 Feb 2009 ;; Version: 1.0 ;; Keywords: ;; 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, 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. ;; A copy of the GNU General Public License can be obtained from this ;; program's author (send electronic mail to oub@mat.ucm.es) or from ;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA ;; 02139, USA. ;;; Commentary: ;;; Code: (require 'matlab) (require 'matlab-shell) ;; Variables (defvar matlab-temp-region-file "region.m" "*Variable for the file which saves the region for publishing.") (defvar matlab-show-matlab-shell t "* t means the matlab buffer shell is shown when running matlab.") ;; Functions (defun matlab-select-publish-form (ch) "This function allows to publish the m file, either as in LaTeX or in HTML format." (interactive "c1: LaTeX, 2: HTML ") (setq ch (downcase ch)) (call-interactively (cond ((eql ch ?1) #'matlab-publish-file-latex) ((eql ch ?2) #'matlab-publish-file-html) (t (error 'args-out-of-range '(1 2 ch)))))) (defun matlab-publish-file-latex () "Publish a matlab file in the LaTeX format." (interactive) (let ((pub (file-name-nondirectory (buffer-file-name)))) (matlab-shell-run-command (format "publish('%s','latex')" pub))) (if matlab-show-matlab-shell (matlab-show-matlab-shell-buffer))) (defun matlab-publish-file-html () "Publish a matlab file in the html format." (interactive) (let ((pub (file-name-nondirectory (buffer-file-name)))) (matlab-shell-run-command (format "publish('%s','html')" pub))) (if matlab-show-matlab-shell (matlab-show-matlab-shell-buffer))) (defun matlab-select-environment (ch) "Inserts structured text. This results in LaTeX mode in title, sections, description, boldified text, unnumbered equations and bullet list." (interactive "c1: title, 2: section, 3:descrip, 4:boldify, 5:equation, 6:list ") (setq ch (downcase ch)) (call-interactively (cond ((eql ch ?1) #'matlab-insert-title) ((eql ch ?2) #'matlab-insert-section) ((eql ch ?3) #'matlab-insert-description-text) ((eql ch ?4) #'matlab-boldify) ((eql ch ?5) #'matlab-insert-equation) ((eql ch ?6) #'matlab-insert-bullet-list) (t (error 'args-out-of-range '(1 2 3 5 6 ch)))))) (defun matlab-insert-title () (interactive) (goto-char (point-min)) (insert "%% TITLE\n") (forward-line -1) (forward-char 3)) (defun matlab-insert-section () (interactive) (insert "%% Section\n") (forward-line -1) (forward-char 3)) (defun matlab-insert-description-text () (interactive) (insert "%%\n") (insert "% DESCRIPTIVE TEXT\n") (forward-line -1) (forward-char 2)) (defun matlab-boldify () "Insert either \%\%\\n \% \*BOLD TEXT\*\\n or, when mark is active, surrounds region by * *." (interactive) (if (or (and (boundp 'zmacs-region-active-p) zmacs-region-active-p) (and (boundp 'transient-mark-mode) transient-mark-mode mark-active)) (save-excursion (goto-char (point)) (insert "*") (goto-char (mark)) (insert "*")) (insert "\n%%\n") (insert "% *BOLD TEXT*\n") (backward-char 2))) (defun matlab-insert-bold-text () (interactive) (insert "%%\n") (insert "% *BOLD TEXT*\n") (forward-line -1) (forward-char 2)) (defun matlab-insert-monospaces-text () (interactive) (insert "%%\n") (insert "% |MONOSPACED TEXT|\n") (forward-line -1) (forward-char 2)) (defun matlab-insert-preformated-text () (interactive) (insert "%%\n") (insert "%\n") (insert "% PREFORMATTED\n") (insert "% TEXT\n") (insert "% \n") (forward-line -3) (forward-char 3)) (defun matlab-insert-equation () (interactive) (insert "%%\n") (insert "% \n") (insert "% $$e^{\pi i} + 1 = 0$$\n") (insert "% \n") (forward-line -2) (forward-char 4)) (defun matlab-insert-bullet-list () (interactive) (insert "%%\n") (insert "% \n") (insert "% * ITEM1\n") (insert "% * ITEM2\n") (insert "% \n") (forward-line -3) (forward-char 4)) (defun matlab-write-region (start end) (interactive "r") (write-region start end (expand-file-name matlab-temp-region-file) nil nil nil nil) (find-file matlab-temp-region-file)) (defun matlab-write-region-novisit (start end) (interactive "r") (write-region start end (expand-file-name matlab-temp-region-file) nil nil nil nil)) (defun matlab-publish-region (start end ch) (interactive "r\nc1: LaTeX, 2: HTML ") (setq ch (downcase ch)) (write-region start end (expand-file-name matlab-temp-region-file) nil nil nil nil) (call-interactively (cond ((eql ch ?1) #'matlab-publish-region-latex) ((eql ch ?2) #'matlab-publish-region-html) (t (error 'args-out-of-range '(1 2 ch)))))) (defun matlab-publish-region-latex () (interactive) (matlab-shell-run-command (format "publish('%s','latex')" matlab-temp-region-file)) (if matlab-show-matlab-shell (matlab-show-matlab-shell-buffer))) (defun matlab-publish-region-html () (interactive) (matlab-shell-run-command (format "publish('%s','html')" matlab-temp-region-file)) (if matlab-show-matlab-shell (matlab-show-matlab-shell-buffer))) (provide 'matlab-publish) ;;{{{Change log: ;; $Id$ ;; $Log$ ;; Revision 1.1 2009-07-06 19:59:10 zappo ;; Utilities for editing MATLAB files for publishing ;; ;; Revision 1.9 2009/03/08 09:38:31 oub ;; * matlab-publish.el (matlab-select-environment): change function ;; slightly ;; (matlab-insert-section): new function ;; ;; Revision 1.8 2009/03/07 13:54:57 oub ;; (matlab-write-region-novisit): New function ;; (matlab-publish-region-old): new function ;; (matlab-publish-region): New function ;; (matlab-publish-region-latex): new function ;; (matlab-publish-region-html): new function ;; ;; Revision 1.7 2009/03/07 13:26:55 oub ;; (matlab-show-matlab-shell): New variable ;; (matlab-publish-file-latex): add ;; (if matlab-show-matlab-shell ;; (matlab-show-matlab-shell-buffer))) ;; ;; Revision 1.6 2009/03/07 11:00:04 oub ;; * matlab-publish.el (matlab-boldify): Modify the function. ;; ;; Revision 1.5 2009/03/06 14:24:47 oub ;; * matlab-publish.el (matlab-temp-region-file): new variable ;; (matlab-write-region): New function ;; ;; Revision 1.4 2009/02/26 16:18:23 oub ;; modifiy the insert functions in order that the jump to the text where ;; new stuff has to be inserted. ;; ;; Revision 1.3 2009/02/26 16:06:08 oub ;; (defun matlab-select-environment (ch) ;; (interactive "c1: title, 2: descr, 3: bold:, 4:mono, 5:pre, 6:equation, 7:list ") ;; (setq ch (downcase ch)) ;; (call-interactively (cond ((eql ch ?1) #'matlab-insert-title) ;; ((eql ch ?2) #'matlab-insert-description-text) ;; ((eql ch ?3) #'matlab-insert-bold-text) ;; ((eql ch ?4) #'matlab-insert-monospaces-text) ;; ((eql ch ?5) #'matlab-insert-preformated-text) ;; ((eql ch ?6) #'matlab-insert-equation) ;; ((eql ch ?7) #'matlab-insert-bullet-list) ;; (t (error 'args-out-of-range '(1 2 3 5 6 7 ch)))))) ;; ;; Revision 1.2 2009/02/26 11:24:17 oub ;; New functions ;; (defun matlab-insert-title () ;; ;; (defun matlab-insert-description-text () ;; ;; ;; (defun matlab-insert-bold-text () ;; ;; ;; (defun matlab-insert-monospaces-text () ;; ;; ;; ;; (defun matlab-insert-preformated-text () ;; ;; ;; (defun matlab-insert-equation () ;; ;; ;; (defun matlab-insert-bullet-list () ;; ;; Revision 1.1 2009/02/26 11:15:02 oub ;; Initial revision ;; ;;}}} (provide 'matlab-publish) ;;; matlab-publish.el ends here matlab-mode/NEWS.org0000664000175000017500000001773714611713120015256 0ustar sebastiensebastien* 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 Emacs debugging of MATLAB code is back! : M-x matlab-shell : >> dbstop in file.m This is a significant improvement over the version that used to exist years ago. There are many improvements and fixes. Full debugging support is provided. You can debug MATLAB scripts, functions, packages, and classes. The debugger integration is using Emacs gud. You have the following capabilities when in a MATLAB M-file: : C-x C-a C-b Set a breakpoint on current source line (dbstop) : C-x C-a C-d Delete the breakpoint(s) on the current source line (dbclear) : C-x C-a C-s Step a single line of code, stepping into functions (dbstep in) : C-x C-a C-n Next a single line of code stepping over functions (dbstep) : C-x C-a < Move up the next enclosing stack frame (dbup) : C-x C-a > Move down the next inner stack frame (dbdown) : C-x C-a C-f Finish, quit (dbquit) *** matlab-shell: News 1. matlab-shell editing of M-files now opens the M-file in Emacs without requiring setup. : >> edit file.m will open file.m in your Emacs session. To do this, matlab-shell leverages the Emacs server mode to have MATLAB send the file to be edited to Emacs. matlab-shell will launch a unique server process for you if needed. 2. In matlab-shell, errors are hyperlinked and left-mouse clickable (in addition to middle or RET clickable). Errors are also shown in an error font. 3. In matlab-shell, RET on output lines (i.e. non-command lines) no longer causes lots of unnecessary noise. 5. In matlab-shell, tab completion is much more responsive. matlab-shell is compatible with company-mode. If you have install company-mode, you will get tab completion pop-ups. 6. In matlab-shell, "run cell" and "run region" capabilities are now executed in the base workspace. *** Menu - The MATLAB menu for M-files is now always present. Prior, there would be cases where the menu disappears. The MATLAB menu also contains a new Debug sub-menu. *** Performance - There are a number of performance improvements, meaning emacs is more responsive. For example, in matlab-shell, Emacs is more responsive when processing long output lines from MATLAB. *** Bug fixes - There are a number of bug fixes. *** Quotes - Single and double quote strings are now supported. MATLAB calls single-quote strings char array's and double quote strings are referred to as strings. *** Prog mode Matlab mode is now a derived mode, based on /prog mode/. *** Support for older Emacsen Support for Emacsen older than 20 has been removed. Xemacs supports has not touched, but has also not been tested, so it might be buggy. The current Matlab version has been successfully compiled with GNU emacs 24,25,26 and 27. There have been difficulties to compile it with Xemacs 21.4.X or 21.5.X, this issue is currently not resolved. *** Loading Faster load time by moving rarely used content into other files. *** Completion TAB completion in the shell was revamped (but should act mostly the same) matlab-mode/cedet-matlab.el0000664000175000017500000000362614611713120016625 0ustar sebastiensebastien;;; cedet-matlab.el --- CEDET Setup support ;; ;; Copyright (C) 2009, 2012, 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; X-RCS: $Id$ ;; ;; 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, 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; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Setup miscelaneous CEDET tools to work with MATLAB. (eval-when-compile (require 'semantic) (require 'srecode/map)) ;;; Code: ;;;###autoload (defun matlab-cedet-setup () "Setup support for CEDET tools for use with MATLAB." (interactive) ;; Setup Semantic parser: (add-hook 'matlab-mode-hook 'semantic-default-matlab-setup) ;; Setup semanticdb for MATLAB support: (require 'semanticdb-matlab) ;; Setup Semantic Recoder (Template support for MATLAB and TLC.): (let* ((lib (locate-library "matlab.el" t)) (ededir (file-name-directory lib)) (tmpdir (file-name-as-directory (expand-file-name "templates" ededir)))) (when (not tmpdir) (error "Unable to locate MATLAB Templates directory")) ;; Rig up the srecode map. (require 'srecode/map) (add-to-list 'srecode-map-load-path tmpdir) (if (fboundp 'srecode-map-update-map) (srecode-map-update-map t) (error "Function srecode-map-update-map not found")) )) (provide 'cedet-matlab) ;;; cedet-matlab.el ends here matlab-mode/company-matlab-shell.el0000775000175000017500000000715314611713120020316 0ustar sebastiensebastien;;; company-matlab-shell.el --- a matlab-shell-mode completion back-end for Matlab ;; ;; Copyright (C) 2016,2017,2019 Eric Ludlam ;; Copyright (C) 2009 David Engster ;; ;; 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 . (condition-case nil (require 'company) (error nil)) (eval-when-compile (require 'cl)) (require 'matlab) (require 'matlab-shell) (defvar company-matlab-shell--ci (make-hash-table :test 'equal) "Private variable for company-matlab-shell completion info") (defun company-matlab-shell-grab-completion-substr () "Return the completion substring of the command that is to be completed in `matlab-shell', or 'stop if completions can't be performed at the current point." (when (eq major-mode 'matlab-shell-mode) (if (not (matlab-on-prompt-p)) 'stop ;; tell company can't complete when point is not in the prompt (let ((lastcmd (buffer-substring (point) (matlab-point-at-eol)))) ;; Kill the rest of the line since completion only works at the end of the line and ;; we'd like it to complete within a line. For example, ;; h=figure; ;; h.set('Vis','on') ;; ^ ;; TAB here results in: ;; h.set('Visible','on') (delete-region (point) (matlab-point-at-eol)) (let* ((buf-name (buffer-name (current-buffer))) (ci (matlab-shell-get-completion-info)) (common-substr (cdr (assoc 'common-substr ci))) (did-completion (cdr (assoc 'did-completion ci)))) ;; restore the killed part of the line (let ((orig-pt (point))) (insert lastcmd) (goto-char orig-pt)) ;; If did-completion, then matlab-shell-get-completion-info updated the ;; *MATLAB* buffer by deleting text and calling (insert replacement-text), and ;; we have no more completion info. (if did-completion ;; Tell company to abort completion. This causes "Cannot complete at point" and ;; there doesn't seem to be a way to protect against this message. nil (puthash buf-name ci company-matlab-shell--ci) ;; command to be completed common-substr )))))) (defun company-matlab-shell-get-completions () (let* ((ci (if (eq major-mode 'matlab-shell-mode) (gethash (buffer-name (current-buffer)) company-matlab-shell--ci))) (completions (if ci (cdr (assoc 'completions ci))))) (if ci (remhash (buffer-name (current-buffer)) company-matlab-shell--ci)) (mapcar 'car completions))) ;;;###autoload (defun company-matlab-shell (command &optional arg &rest ignored) "A `company-mode' completion backend for `matlab-shell'." (interactive (list 'interactive)) (case command ('interactive (if (fboundp 'company-begin-backend) ;; quiet warning when no company (company-begin-backend 'company-matlab-shell) (error "company-begin-backend is missing"))) ('prefix (company-matlab-shell-grab-completion-substr)) ('candidates (company-matlab-shell-get-completions)) ('sorted t))) (provide 'company-matlab-shell) ;;; company-matlab-shell.el ends here matlab-mode/matlab-maint.el0000664000175000017500000001533314611713120016647 0ustar sebastiensebastien;;; matlab-maint.el --- matlab mode maintainer utilities ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; Interface for maintainers of matlab mode. (require 'matlab) (require 'matlab-shell) (require 'matlab-netshell) (require 'semantic/symref) (require 'semantic/symref/list) ;;; Code: ;;; Minor Mode Definition ;; (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.") (easy-menu-define matlab-maint-menu matlab-maint-mode-map "MATLAB Maintainer's Minor Mode" '("MMaint" ["Compile" matlab-maint-compile-matlab-emacs t] ["Clean" matlab-maint-compile-clean t] ["Run Tests" matlab-maint-run-tests t] ["Pick Emacs to run" matlab-maint-pick-emacs t] ["Toggle IO Logging" matlab-maint-toggle-io-tracking :style toggle :selected matlab-shell-io-testing ] ["Display logger frame" matlab-maint-toggle-logger-frame :style toggle :selected (and matlab-maint-logger-frame (frame-live-p matlab-maint-logger-frame)) ] )) ;;;###autoload (define-minor-mode matlab-maint-minor-mode "Minor mode for matlab-mode maintainrs." nil " MMaint" matlab-maint-mode-map ) ;;;###autoload (define-global-minor-mode global-matlab-maint-minor-mode matlab-maint-minor-mode (lambda () "Should we turn on in this buffer? Only if in the project." (let ((dir (expand-file-name default-directory)) (ml (file-name-directory (expand-file-name (locate-library "matlab"))))) (when (string= ml (substring dir 0 (min (length dir) (length ml)))) (matlab-maint-minor-mode 1)))) ) ;;; 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") "Various emacs versions we can use to compile with." :group 'matlab-maint :type '(repeat (string :tag "Emacs Command: "))) (defcustom matlab-maint-compile-emacs "emacs" "The EMACS to pass into make." :group 'matlab-maint :type 'string) (defun matlab-maint-pick-emacs (emacscmd) "Select the Emacs to use for compiling." (interactive (list (completing-read "Emacs to compile MATLAB: " matlab-maint-compile-opts nil t (car matlab-maint-compile-opts)))) (setq matlab-maint-compile-emacs emacscmd) ) (defun matlab-maint-compile-matlab-emacs () "Run make for the matlab-emacs project." (interactive) (save-excursion (matlab-maint-set-buffer-to "matlab.el") (if (string= matlab-maint-compile-emacs "emacs") (compile "make") (compile (concat "make EMACS=" matlab-maint-compile-emacs)))) ) (defun matlab-maint-compile-clean () "Run make for the matlab-emacs project." (interactive) (save-excursion (matlab-maint-set-buffer-to "matlab.el") (compile "make clean") )) (defun matlab-maint-run-tests (arg) "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) ;; Ask for dbug (compile "make TESTDEBUG=1") ;; No debugging (compile "make"))) (switch-to-buffer "*compilation*") (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. Return the buffer." (let* ((ml (file-name-directory (locate-library "matlab"))) (newf (expand-file-name file ml))) (set-buffer (find-file-noselect newf)))) (defun matlab-maint-toggle-io-tracking () "Toggle tracking of IO with MATLAB Shell." (interactive) (setq matlab-shell-io-testing (not matlab-shell-io-testing)) (message "MATLAB Shell IO logging %s" (if matlab-shell-io-testing "enabled" "disabled"))) (defvar matlab-maint-logger-frame nil "Frame displaying log information.") (defun matlab-maint-toggle-logger-frame () "Display a frame showing various log buffers." (interactive) (if (and matlab-maint-logger-frame (frame-live-p matlab-maint-logger-frame)) (progn (delete-frame matlab-maint-logger-frame) (setq matlab-maint-logger-frame nil)) ;; Otherwise, create ... (setq matlab-maint-logger-frame (make-frame)) (with-selected-frame matlab-maint-logger-frame (delete-other-windows) (switch-to-buffer "*Messages*") (when (matlab-netshell-client) (split-window-horizontally) (other-window 1) (switch-to-buffer (process-buffer (matlab-netshell-client))) )))) (provide 'matlab-maint) ;;; matlab-maint.el ends here matlab-mode/Makefile0000664000175000017500000000402314611713120015411 0ustar sebastiensebastien# Automatically Generated Makefile by EDE. # For use with: make # Relative File Name: Makefile # # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST. # EDE is the Emacs Development Environment. # http://cedet.sourceforge.net/ede.shtml # top="$(CURDIR)"/ ede_FILES=Project.ede Makefile EMACS=emacs EMACSFLAGS=-batch --no-site-file --eval '(setq debug-on-error t)' 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-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) all: autoloads misc lisp cedet toolbox Templates .PHONY: clean-autoloads clean-autoloads: rm -f $(LOADDEFS) .PHONY: autoloads autoloads: $(EMACS) $(EMACSFLAGS) $(addprefix -L ,$(LOADPATH)) --eval '(setq generated-autoload-file "$(abspath $(LOADDEFS))")' -f batch-update-autoloads $(abspath $(LOADDIRS)) misc: @ %.elc: %.el $(EMACS) $(EMACSFLAGS) $(addprefix -L ,$(LOADPATH)) --eval '(progn $(call require, $(PRELOADS)))' -f batch-byte-compile $^ .PHONY: lisp lisp: $(addsuffix c, $(lisp_LISP)) .PHONY: cedet cedet: $(addsuffix c, $(cedet_LISP)) .PHONY:toolbox toolbox: $(MAKE) -C toolbox .PHONY:Templates Templates: $(MAKE) -C templates tags: $(MAKE) -C toolbox/ $(MFLAGS) $@ $(MAKE) -C templates/ $(MFLAGS) $@ clean: rm -f *.elc .PHONY: dist dist: autoloads rm -rf $(DISTDIR) mkdir $(DISTDIR) cp matlab-load.el $(misc_MISC) $(lisp_LISP) $(cedet_LISP) $(ede_FILES) $(DISTDIR) $(MAKE) -C toolbox $(MFLAGS) DISTDIR=$(DISTDIR)/toolbox dist $(MAKE) -C templates $(MFLAGS) DISTDIR=$(DISTDIR)/templates dist $(MAKE) -C tests $(MFLAGS) DISTDIR=$(DISTDIR)/tests dist tar -cvzf $(DISTDIR).tar.gz $(DISTDIR) rm -rf $(DISTDIR) # End of Makefile matlab-mode/.gitignore0000664000175000017500000000015014611713120015736 0ustar sebastiensebastien# -*-conf-*- *.elc *~ \#*\# .\#* *.orig *.rej *.dat # matlab-load.el is generated matlab-load.el parsematlab-mode/matlab-mode-pkg.el0000664000175000017500000000046314611713120017240 0ustar sebastiensebastien;;; matlab-pkg.el --- define matlab for package.el (define-package "matlab-mode" "4.0" "Major mode for MATLAB(R) dot-m files" 'nil :keywords '("matlab" "programming" "language" "(X)emacs") :url "http://sourceforge.net/projects/matlab-emacs/" ) ;; Local Variables: ;; no-byte-compile: t ;; End: matlab-mode/ChangeLog0000664000175000017500000010500014611713120015520 0ustar sebastiensebastien2023-07-12 22:14 Uwe Brauer * COPYING: add GPL3+ License 2023-07-12 Uwe Brauer * cedet-matlab.el: update to GPL3+ * company-matlab-shell.el: ditto * company-matlab-shell.el: ditto * dl_emacs_support.m: ditto * linemark.el: ditto * matlab-publish.el: ditto * matlab.el: ditto * mlint.el: Ditto * semantic-matlab.el: Ditto * semanticdb-matlab.el: ditto 2023-04-24 14:47 Uwe Brauer * matlab.el (matlab-mode): Apply patch provided by Peter Mao 2022-10-08 18:19 Uwe Brauer * matlab.el (matlab-mode): Apply patch provided my Eric Ludlam: delete the line (kill-all-local-variables) in the matlab-mode function. 2022-04-12 Uwe Brauer * matlab-load.el (matlab-mode): Add the load file to the repository, for the MELPA archive. 2022-04-12 Uwe Brauer * matlab-syntax.el (matlab--transpose-syntax): 680 Apply patch provided by Eric Ludlam to fix a transpose syntax problem 2022-03-11 John Ciolfi * matlab-netshell.el, matlab-shell-gud.el, matlab-shell.el, matlab.el, mlgud.el Fix conflict between matlab-shell debugging and C++ (or other language) debugging. - matlab-shell debugging previously leveraged gud.el for debugging of *.m files. - C++ debugging also leverages gud.el for debugging. - Only one instance of a gud.el debugger can be active. If you run two, say *.m debugging and C++ debugging. Then you get odd errors such as the ebbreak not being recognized by gdb. To fix these issue, I copied gud.el and made a new namespace "mlgud". I also removed a lot of unused code from mlgud.el, though there's still more that can be removed. Now one can debug *.m files and *.cpp files in one Emacs session. 2021-11-22 Uwe Brauer * company-matlab-shell.el: 677 correct a silly typo 2021-07-26 Uwe Brauer * matlab-shell.el (matlab-shell-region->script): 676 apply patch provided by Karthik Chikmagalur : Copy all local functions to script. 2021-05-04 Uwe Brauer * toolbox/emacsrunregion.m (emacsrunregion): Apply a patch provided by Julien Claisse: trump support 2021-02-25 John Ciolfi ciolfi@mathworks.com * tlc.el (new version 1.3): revamped implementation 1. Make tlc indent by 4 spaces. Previously we were indenting by 2 spaces as this was used when we created tlc. Updating to 4 to be consistent with matlab-mode, etc. 2. Don't indent "TLC file guards" %if EXISTS(::_FILE_NAME_) == 0 %assign _FILE_NAME_ = 1 %endif 3. Don't indent when inside of multi-line comments, fixed multi-line comment detection 4. Various indent fixes 5. Always align %if/%elseif/%else/%endif statements, even when unbalanced "{", "}" language elements exist. 6. Add support for special "%%{", "%%{{", "%%}", "%%}}", etc. 'indent comment shift operators' adjust indentation by 4, 8, -4, -8, etc. 7. Add support for "%%{N}" 'indent comment shift operators' that adjust what follows to indent to column N 8. Fix the tlc syntax table to correctly handle single and multiline comments 9. Make M-; use single line "%% ..." comments 10. Improved (TAB) indent-region performance by simplify logic and only checking for multiline comments when required. 2020-01-06 Uwe Brauer * matlab-compat.el (matlab-find-executable-directory): Apply patch provided by John Ciolfi , thanks John. 2019-11-27 Uwe Brauer * NEWS.org (News in 4.0): 290 Merge, documentation concerning new features, provided by John Ciolfi , thanks John. 2019-11-25 Uwe Brauer * NEWS.org (Changes and New Features in matlab-emacs): New file, add News for 4.0 2019-09-30 Eric Ludlam * matlab.el: String & Comment Font Lock Handling (matlab-mode-syntax-table): Add " as puncutation. (matlab-string-char-regexp): New (matlab-string-start-regexp): Add " (matlab-string-content-regexp, matlb-string-end-regexp) (matlb-match-string-end-for-string) (mtlab-font-lock-string-match-normal) (matlab-font-lock-string-match-unterninated) (matlab-font-lock-string-match-here) (matlab-font-lock-comment-match, ): Deleted (matlab-font-lock-string-start-regexp): New (matlab-font-lock-string-and-comment-start-regexp): New (matlab-font-lock-allstring-comment-match-normal): New (matlab-test-allstring-comment-match): New interactive tester for fontlocked strings and comments. * matlab.el Misc Font Lock improvements (matlab-handle-simulink): Remove option. Always highlight simulink keywords. (matlab-keyword-list): Remove classdef - this is handled diretly. (matlab-simulink-keywords, matlab-constants-keyword-list): New, moved out of explicit font lock expression. (matlab-font-lock-regexp-opt): New fcn used in place of regepxp-opt to simplify font-lock keywords, and maintain compatibility. (matlab-font-lock-keywords): Replace old charvect and comment matchers with new unified string and comment matcher. Replace all calls to regexp-opt with matlab-font-lock-regexp-opt. Replace hard-coded lists of keywords with call to *-regexp-opt. (matlab-function-font-lock-keywords): New, derived from gaudy keywords. (matlab-class-attributes-list-re,matlab-class-font-lock-keywords): New (matlab-gaudy-font-lock-keywords): Now built from function and class keywords list. (matlab-realy-guady-font-lock-keywords): Replace obsolete font-lock-type-face. Remove continuation expression (now handled in combined string/comment highlighter). Replace all calls to regexp-opt with matlab-font-lock-regexp-opt. (matlab-mode): Add support for font-lock-multiline. If show-paren-mode is available, use that instead of built-in block highlighting. (matlab-ispell-strings-region): Use new font lock matcher for strings. (matlab-valid-end-construct-p): Don't modify match-data. * matlab.el Block and Comment navigation (matlab-up-string-or-comment, matlab-backward-up-string-or-comment): New (matlab-move-list-sexp-internal): New (only list like expressions) (matlab-move-simple-sexp-backward-internal): New (call below) (matlab-move-simple-sexp-internal): New, nav comments, strings, and lists. (matlab-backward-sexp): Nav simple-sexp (as above). Fix bug with noerror version if there are stacks of incomplete ends. (matlab-forward-sexp): Nav w/ simple-sexp (as above) instead of using built-in forward-sexp when not navigating blocks. (matlab-forward-sexp): Add 'autostart' optional input. (matlab-lattr-block-close): Account for ends in comments and strings. (matlab-show-cursor-context): New debug command for testing what is under the cursor. (matlab-cursor-comment-string-context): New detector fcn for strings and comments. (matlab-ursor-in-string-or-comment, matlab-cursor-in-comment) (matlab-cursor-in-string): Delete impl, use above detector instead. * matlab.el: show-paren-mode support (matlab-show-paren-or-block): Function called from show-paren-mode to identify what to highlight. Use new navigation system to identify matching parens and block keywords. * tests/metest.el: New set of Emacs tests. * tests/metest.sh: Shell script to run tests. * tests/strings.el, tests/expressions.m, tests/mclass.m: New MATLAB files with various syntaxes and test tokens used to verify new behavior. 2018-09-28 Uwe Brauer * matlab.el (matlab-string-start-regexp): Patch provided by Eric Ludlam: adds support for Strings in matlab-mode. b = "string scalar" and b = "string "" scalar" are now highlighted. 2018-01-25 John Ciolfi * matlab.el, company-matlab.el: Allow completion within a line and better quit support. Patch provided by Nate Chodosh. 2017-12-24 John Ciolfi * Makefile: Updated byte compile to work on Emacs 25, other minor cleanups. 2017-12-05 John Ciolfi * README.org: Revamp help. Remove stale items. * matlab.el, emacsinit.m: make matlab-shell-emacsclient-command customizable so people can easily tailor the ">> edit file.m" behavior. 2017-12-05 John Ciolfi * TAB: Fixed several problems with TAB completion, and added direct support for company (complete anything) mode tab completion. If company is installed TAB key is bound to use company completion and C-TAB is classic emacs TAB completion. * Debugging: Enabled debugging of matlab code (dbstop, etc.) in R2015b MATLAB and later. This requires using the MATLAB editor instead of emacs because recent MATLAB's are not giving enough info to debug within emacs. * Warnings: Fixed/suppressed all warnings as reported by emacs 24.4 (and removed support for emacs 22 and earlier). * Makefile: matlab-load.el is a generated file that should be rebuilt after clean. * matlab-load.el: remove this from the repro. It is generated so it should be rebuilt via GNU make. * company-matlab-shell.el: made completion using company (complete anything) work with new TAB handling paradigm * linemark.el: fixed compiler warnings * matlab-publish.el: fixed compiler warnings * matlab.el: fixed TAB completion handling. Prior to fix, hitting tab would generate noise (new unnecessary ">>" prompts) and it didn't handle certain cases, e.g. ">> ! mv file.", ">> set(h,'')", etc. would fail to complete. * mlint.el: fixed compiler warnings * cedet-matlab.el, semantic-matlab.el, semanticdb-matlab.el: fixed warnings, though I'm not sure if these files are still usable because some functions they reference no longer exist. * tlc.el: fixed compiler warnings, including autoload issue that was preventing use. * toolbox/emacsdocomplete.m: fixed handling of tab completion for commands with strings * toolbox/emacsinit.m: don't activate emacs-based debugging/ dbstop handling in R2015b and later. 2016-04-15 Uwe Brauer * matlab-pkg.el ("matlab-mode"): New file for the MELPA repo: purpose: add information to the GNU Emacs package system. User of older GNU Emacs versions <24, and Xemacs users can safely ignore this file. 2016-04-10 Uwe Brauer * matlab.el: Add an empty line after the first line in matlab.el, maybe this will generate the desired info line when calling package-list-package in GNU emacs. (MELPA). 2016-04-01 Uwe Brauer * matlab.el (matlab-mode-version): change version to 3.3.5 * matlab.el (matlab-enable-block-highlighting): Applied patch provided by kaushal_modi@users.sf.net 2016-03-17 Uwe Brauer * toolbox/dbhotlink.m (dbhotlink): new file 2016-03-17 Uwe Brauer * matlab.el (matlab-shell-mode): Applied patch from Odd Andersen . 2016-03-16 Uwe Brauer * dl_emacs_support.m (getfiles): update the url and README-->README.org 2016-02-09 Uwe Brauer * matlab.el (matlab-mode-version): Change version number to 3.3.3 * matlab-publish.el (matlab-select-publish-form): Cleanup file * matlab.el (matlab-change-current-directory): Applied patch, from an unknown source found in http://sourceforge.net/p/matlab-emacs/patches/2/ Purpose: add the possibility to switch the matlab directory to the current one. * matlab.el (matlab-shell): Applied patch from http://sourceforge.net/p/matlab-emacs/patches/2/ which results in (define-key km (kbd "TAB") 'matlab-shell-tab) 2016-02-09 Uwe Brauer Imported from CVS to git. 2014-11-05 zappo * Makefile: (VERSION) Updated. (all, tags, dist): Add toolbox (toolbox): New rule * Project.ede (:version): Updated * templates/Makefile (VERSION): Update to minor rev. * toolbox/Makefile: Makefile for toolbox. * toolbox/Project.ede: Project file for toolbox. * INSTALL (tlc.el): Fix typo. * matlab.el (matlab-shell-completion-list): remove custom call to MCR with call to emacsdocomplete.m in the matlab-emacs toolbox directlry. * toolbox/emacsdocomplete.m: Command for getting completions from MATLAB for matlab-shell. 2014-11-05 zappo * matlab.el (matlab-shell-completion-list): remove custom call to MCR with call to emacsdocomplete.m in the matlab-emacs toolbox directlry. * toolbox/emacsdocomplete.m: Command for getting completions from MATLAB for matlab-shell. 2014-09-24 zappo * .cvsignore: First checkin. 2014-09-23 zappo * matlab.el (matlab-shell-font-lock-keywords): Tweak to support some changes in error output. (matlab-comment): For comment on same line. If we run out of space (fill-column) pull the comment closer to the code. (matlab-auto-fill): When filling a string, improve checking of when to add brackets around the string. (matlab-shell-mode): Disable html rendering trickery. (gud-matlab-error-regexp): Add a new variant (off by default) as reminder to go back and get the current ones to handle newer MATLAB error format. 2014-03-07 zappo * mlint.el (mlint-minor-mode): Remove make-local-hook for Emacs 24. 2013-08-29 zappo * semantic-matlab.el (semantic/dep): New dependency (Emacs 24) (semantic-matlab-parse-oldstyle-class): Add 'method' local var to tidy compile warnings. 2013-08-26 zappo * matlab.el (matlab-mode-version): Bump very-minor versin. (matlab-ltype-comm): Add support for block comment detection. (matlab-ltype-block-comm): New fcn. (matlab-shell): Add TAB binding for TAB for when (tab) doesn't work. Add support for setting WINDOWID environment variable so that MATLAB will bring Emacs forward when typing in a figure. 2013-04-02 zappo * README: Update to discuss CEDET that comes with Emacs. * INSTALL: Update to note latest build notes. * Makefile: Regenerated Added CEDET_PATH variable if someone wants to download the latest from bzr. * Project.ede (:version): Updated (lisp): Add linemark.el * matlab-load.el: Regenerated. * dl_emacs_support.m (coreFiles): Add linemark.el to download list. * linemark.el: Add linemark to matlab-emacs dist so that mlint will work with the version of CEDET integrated with Emacs. * cedet-matlab.el (matlab-cedet-setup): Update srecode require to work w/ CEDET integrated w/ Emacs. * semantic-matlab.el (misc require statements): Updated to work w/ CEDET integrated w/ Emacs. * semanticdb-matlab.el (misc require statements): Updated to work w/ CEDET integrated w/ Emacs. (semanticdb-matlab-scan-directories): Remove references to `working'. (semanticdb-matlab-cache-files): Remove references to `working'. * templates/Makefile (VERSION): Updated. 2012-02-23 zappo * matlab.el (matlab-mode): Change page-delimeter to allow any whitespace or \n after a %%. 2011-10-05 zappo * matlab.el (matlab-ltype-endfunction-comm): If the endfunction comment occurs on a line, but the next bit of code isn't part of an enclosing fcn, then no. (matlab-shell-mode): Patch from Eli Merriam; make comint-input-filter-functions buffer local. Add matlab-shell-render-html-txt-format hook to comint. (matlab-txt-format-beg, matlab-txt-format-end): New (matlab-shell-render-html-txt-format): New. (matlab-shell-last-anchor-as-frame): New. (matlab-shell-render-errors-as-anchor): Use last anchor var above. Note: Not used for any logic yet. ;( (matlab-shell-previous-matching-input-from-input): patch; Mark Histed Force cursor to eol. (matlab-shell-run-region): Force displayed buffer to recycled visible buffers. 2011-08-10 davenar * matlab.el (matlab-shell-mode): Add `comint-postoutput-scroll-to-bottom' to `comint-output-filter-functions' (suggested by Mark Histed). 2011-08-09 davenar * matlab.el (matlab-shell-run-region): Fix last commit and correctly use `buffer-substring' again. * matlab.el (matlab-shell-run-region): When removing comments, do not touch matlab strings or we might remove format specifiers. 2011-07-17 davenar * matlab.el (matlab-shell-run-region): Remove all comments before running a region, otherwise `matlab-shell-run-cell' will break. * matlab.el (matlab-shell-run-region): Remove comment lines. If NOSHOW, also remove continuations. * matlab.el (matlab-shell-run-region): New optional argument NOSHOW to replace newlines with commas, so that by default this will not be done. (matlab-shell-run-cell): Use it. 2011-06-16 zappo * matlab.el (matlab-shell-render-html-anchor): Add arbitrary reverse-search limit to improve performance when the output buffer gets large. 2011-03-23 zappo * matlab.el (matlab-shell-use-emacs-toolbox): Fix to use let* * matlab.el (matlab-shell-running-matlab-release): Make more robust if matlab.el is not on the path. (matlab-shell-window-exists-for-display-completion-flag): New flag (matlab-shell-tab): Update to set window exists flag (above) when showing completions, and call hide-completions (below) when done completing. (matlab-shell-tab-hide-completions): New. 2010-12-08 zappo * matlab.el (matlab-mode): Move line that moves point to end of buffer to just in front of where it is used, and inside a save-excursion. 2010-12-06 zappo * matlab.el: Patch from Rudiger Sonderfeld (matlab-frame-init): Add a "switch to" in addition to "start" matlab menu item to make it clear what will happen. 2010-10-20 zappo * mlint.el (mlint-platform): Get smarter about returning the kind of MAC and WIN supported. (mlint-minor-mode): Don't disable verify on save since there is still useful stuff there. 2010-10-14 zappo * dl_emacs_support.m: Patch from: Joe Vornehm (coreFiles): Add matlab-publish.el and company-matlab-shell.el (mktemplatedir,mktoolboxdir): Pay attention to destination argument. 2010-09-14 zappo * matlab.el (matlab-mode): Wrap guessing of indentation in a save-excursion. and running of hooks in a save excursion so point can be moved during checking for the indentation style. (matlab-toggle-show-mlint-warnings) (matlab-toggle-highlight-cross-function-variables): Always call mlint-minor-mode after a change, but pass in a numeric arg to force on or off dependent on the current state. 2010-07-29 zappo * mlint.el (mlint-run): Fix case where mlint-program was buffer local. 2010-07-28 zappo * matlab.el (gud-matlab-error-regexp): Patch from EricW: Fix for more error types. (matlab-shell-run-region): Add hack for replacing CR w/ ,. (matlab-shell-last-error): Fix call to find other window call so 0 is a str. * mlint.el (mlint-program-selection-fcn): New option. (mlint-minor-mode): Use above to select an mlint program. 2010-04-06 zappo * matlab.el (matlab-shell-html-map): Update how it is initialized. Add binding for return (matlab-anchor-beg): Add to expression to remove "matlab:" (matlab-shell-render-html-anchor): Add help-echo. (gud-matlab-error-regexp): Improve for newere MATLABs (matlab-shell-last-error-anchor): New (matlab-shell-render-errors-as-anchor): No longer depend on deleted stack start/end variables which seem to no longer be active. Add help-echo to overlay. Change how 'first' is calculated, apply after the fact. (gud-matlab-marker-filter):Don't collect if prompt hasn't been seen yet. (matlab-shell-html-click): Now use below (matlab-shell-html-go): New from above. 2010-01-21 zappo * INSTALL: Fix path to not say "matlab.el" 2010-01-13 zappo * matlab.el (gud-matlab-marker-filter): Don't filter out backspace. In section collecting whole error strings, wait for \n, not the prompt. 2010-01-05 zappo * matlab.el (matlab-mode-version): Update minor version number. (matlab-indent-function-body): Add 'MathWorks-Standard option. (matlab-functions-have-end-minor-mode): Add code to flip the `matlab-functions-have-end' variable. (matlab-do-functions-have-end-p): New. (matlab-indent-function-body-p): New function. (matlab-mode): First detect if fcns have end, save. Next detect indenting fcn bodies based on 'guess. (matlab-calculate-indentation-1, matlab-next-line-indentation) (matlab-frame-init): Use fcn form of indent-function-body. 2009-12-23 zappo * INSTALL: Fix typo about augmenting the load path. 2009-11-16 zappo * semanticdb-matlab.el (semanticdb-matlab-include-paths): Only set default to ~/matlab if it exists. 2009-09-23 zappo * matlab.el (matlab-shell-tab): Add '.' to list of chars that limit completion. This enables structure completion. 2009-09-15 zappo * README: Change load-path setup line. 2009-09-03 zappo * matlab.el (matlab-shell-mode): Add debugging next, and fix step to step in. * matlab.el (gud-matlab-marker-filter): Remove debug message. * matlab.el (matlab-shell-mode): Add matlab-shell-render-errors-as-anchor to comint-output-filter-functions. (matlab-anchor-beg,matlab-anchor-end) (gud-matlab-marker-regexp-1,gud-matlab-marker-regexp-2): Moved. (gud-matlab-marker-regexp-prefix): Updated. (gud-matlab-error-regexp): Updated. (matlab-last-frame-returned): Deleted. (gud-matlab-error-regexp): Updated. (matlab-shell-render-html-anchor): Remove debugger jumping code. (matlab-shell-error-stack-start, matlab-shell-error-stack-end): New (matlab-shell-render-errors-as-anchor): New. (gud-matlab-marker-filter): Remove support for MATLAB 5 debugging. I don't think it would have worked anyway. (matlab-one-db-request stuff). For frame calculations, query out of gud-marker-acc instead of scanning the buffer which was unreliable. When stripping individual lines from the return buffer, only do so if there is no telltale sign of debugger output. (matlab-url-stack-top-at): New. (matlab-shell-previus-matlab-url):Call above is new arg is t. (matlab-find-other-window-file-line-column): Be robust to more types of file names that might not have .m on the end. (matlab-shell-last-error): Specify there might be a stack, and choose the top. 2009-08-21 zappo * matlab-load.el: Regenerated. * mlint.el (mlint-minor-mode): Add autoload cookie. * matlab.el (mline): Remove require. (matlab-functions-have-end-minor-mode): Remove bogus doc string (copy paste bug). * dl_emacs_support.m: Add toolbox M files, and toolbox constructor. 2009-08-13 zappo * matlab.el (mlint): Add require. (defcustom, etc): Delete old backward compatable stuff for Emacs 19. (matlab-toggle-functions-have-end-minor-mode): Moved to supress byte compile warnings. (matlab-font-lock-nested-function-keyword-match) (matlab-font-lock-cross-function-variables-match): Make overlay a local variable. (matlab-mode): Comments about byte-comp warnings. (gud-matlab-marker-filter,matlab-find-other-window-file-line-column): Use string-to-number. (matlab-shell-run-region-or-line): New new mechanism w/ transient-mark-mode. (matlab-shell-topic-highlight-line): Add comment. * matlab.el: Misc: Add (R) to some occurances of MATLAB. OBSOLETE STUFF: (matlab-hilit19-patterns,fume-function-name-regexp-matlab) (fume-find-next-matlab-function-name,matlab-mode-hilit): Delete (matlab-imenu-generic-expression): Get rid of fume use. INDENT FIX: (matlab-valid-end-construct-p,matlab-lattr-block-close) (matlab-calc-indent,matlab-calculate-indentation) (matlab-calculate-indentation-1): Handle multiple ends on one line. MATLAB EDITOR UPDATE: (matlab-shell-history-file): Calculate from below. (matlab-shell-running-matlab-version, matlab-shell-running-matlab-release) (matlab-shell-use-emacs-toolbox, matlab-shell-emacsclient-command): New variables. (matlab-shell,matlab-shell-hack-logo,matlab-shell-mode) (gud-matlab-marker-filter): Calc version from output. (matlab-shell-version-scrape): New, for above. (matlab-shell-next-matching-input-from-input) (matlab-shell-prev-matching-input-from-input): New commands. (matlab-find-other-window-via-url): Support opentoline links. * toolbox/emacsinit.m, toolbox/opentoline.m: Support calling Emacs from MATLAB via edit commands. 2009-07-07 zappo * INSTALL: Fix LOADPATH discussion. * matlab-load.el: Rebuild using Emacs 23. Adds a provide statement. 2009-07-07 davenar * ChangeLog, semanticdb-matlab.el (semanticdb-matlab-scan-directories): Local bind for working-spinner-display. 2009-07-07 David Engster * semanticdb-matlab.el (semanticdb-matlab-scan-directories): Local bind for working-spinner-display. 2009-07-06 Eric Ludlam * company-matlab-shell.el: Company mode support for matlab-shell. * README: Added notes on CEDET use. Added notes on on the MATLAB download script. * matlab.el (matlab-mode-version): Update. * matlab-publish.el: Utilities for editing MATLAB files for publishing * templates/srecode-matlab.srt: SRecode templates for MATLAB Script. * semantic-matlab.el: Remove automatic init hook. See cedet-matlab.el * cedet-matlab.el: Initialization support for CEDET tools with MATLAB. * dl_emacs_support.m: Convenience download script. * matlab.el (matlab-indent-function-body): Allow a value of 'guess, meaning to guess the indentation style of a pre-existing file. (matlab-keyword-list,matlab-font-lock-keywords) (matlab-block-beg-pre-if,matlab-block-beg-pre-no-if) (matlab-keywords-solo,matlab-quiesce-nosemi-regexp): Add spmd. 2008-10-17 Eric Ludlam * matlab.el: Add enumeration support. 2008-09-18 David Engster * semanticdb-matlab.el (semanticdb-find-tags-by-name-method): Use our own database as fall-back method. (semanticdb-find-tags-for-completion-method): Combine results from MATLAB shell and our own database. 2008-09-08 David Engster * semantic-matlab.el: (semantic-ctxt-current-symbol) (semantic-ctxt-current-symbol-and-bounds): Return nil when no symbol at point. * semantic-matlab.el (semantic-matlab-parse-assignments): Deal with class methods which return same class and be more tolerant with whitespaces. * semantic-matlab.el (semantic-matlab-parse-assignments): Fix bug in parsing limit. Don't parse current line. Deal with class attribute assignments. 2008-09-07 David Engster * semanticdb-matlab.el (semanticdb-matlab-user-class-cache): New variable. (semanticdb-matlab-cache-files): New function for caching files and classes. (semanticdb-matlab-find-name): Use it. (semantic-ctxt-current-class-list): Deal with classes and structures. * semantic-matlab.el: (semantic-matlab-parse-oldstyle-class) (semantic-matlab-find-oldstyle-classes): New functions. (semantic-matlab-parse-region): Cache files. Use new functions for parsing classes. (semantic-matlab-type-hint-string): New variable. (semantic-matlab-parse-assignments): New function. (semantic-get-local-variables): New override. (semantic-ia-insert-tag): Deal with method completions. (semantic-ctxt-current-symbol) (semantic-ctxt-current-symbol-and-bounds): New overrides. * semantic-matlab.el (semantic-matlab-root-directory): Follow symlink. 2008-09-05 Eric Ludlam * semanticdb-matlab.el (semanticdb-find-tags-by-name-method): Fixed to handle case where matlab lies about the doc file location. (semanticdb-find-tags-for-completion-method): Adapt to use matlab-shell when available. * semanticdb-matlab.el (semanticdb-find-tags-by-name-method): Use matlab-shell if available. * semantic-matlab.el (semantic-matlab-root-directory): New Function. (semantic-matlab-function-tags): Update to use above. * matlab.el (matlab-shell-which-fcn): Append .m to builtin. (matlab-shell-matlabroot): New (matlab-shell-collect-command-output): Stop displaying status messages. * matlab.el: (matlab-shell-completion-list): Enabled to work in a non-shell buffer by switching over. (matlab-shell-which-fcn): New. * INSTALL: Revies to new matlab-load.el style. Discuss use w/out the Makefile. Remove semantic-matlab.el doc, it needs a re-write. * Makefile (LOADPATH): Add semantic/bovine * Project.ede (cedet): Add semantic-el dependency. * Makefile (LOADPATH): Now includes semantic. (cedet_LISP): New veriables. (cedet): New target. (dist): Add the cedet support files. * Project.ede ("lisp"): Added versionsource file. ("semantic"): New target * matlab.el (matlab-vers-on-startup): Set default to off. (auto-mode-alist): Add .m files. (matlab-block-end-pre-no-iff): Fix if/else order typo. * README: Update to latest information on installing matlab.el 2008-09-02 Eric Ludlam * mlint.el (mlint-platform): Use string-match to determine 64 bit linux-ness. (mlint-flags): Remove -fix until I get around to supporting it. 2008-09-01 David Engster * semanticdb-matlab.el: Fix doc-strings. * semantic-matlab.el (semantic-idle-summary-function): Use defvar-mode-local instead of make-local-variable. 2008-08-31 David Engster * semantic-matlab.el (semantic-format-tag-prototype): New overload for matlab-mode. (semantic-idle-summary-format-matlab-mode): New function, also displays doc-string. (semantic-ia-insert-tag): Use it. (semantic-default-matlab-setup): Bind `semantic-idle-summary-function'. * semantic-matlab.el: Unconditional require of semanticdb-matlab. (semantic-matlab-function-tags): Better parsing of doc strings. Return flag for builtin functions. (semantic-matlab-sort-raw-tags): Include new :builtin attribute. 2008-08-30 David Engster * INSTALL: Added section for semantic-matlab.el * semantic-matlab.el (semantic-matlab-system-paths-include): New variable. (semantic-matlab-dependency-system-include-path): Use it. (semantic-matlab-root-directory): Take everything until '/bin' as MATLAB root. * semanticdb-matlab.el: New file. Semantic database extensions for MATLAB. 2008-08-22 Eric Ludlam * semantic-matlab.el (semantic-matlab-dependency-system-include-path): Value should be a list. * semantic-matlab.el: Changes contributed by: David Engster (semanticdb-matlab): Conditional load. (semeantic-matlb-root-directory): New variable (semantic-matlab-match-function-re): Support _ (semantic-matlab-function-tags): Support loading doc strings. (semantic-matlab-sort-raw-tags): Support doc strings. (semantic-matlab-dependency-system-include-path) (semantic-matlab-display-docstring): New variables (semantic-ia-insert-tag): New overload function. 2008-08-05 Eric Ludlam * matlab.el (matlab-shell-ask-MATLAB-for-completions): Change default to t. Patch from David Engster: (matlab-shell-completion-list): Set scroll-show-maximum-output to nil. (matlab-shell-tab): Improve completion list extraction. 2008-08-01 Eric Ludlam * mlint.el (mlint-symtab-info): New variable. (mlint-run): Init symtab local var. Fixed symbol table parsing to work with newer mlint. * matlab.el (matlab-block-indent-toc-toc-flag): New variable. Default nil. (matlab-block-beg-pre-if, matlab-block-beg-pre-no-if) (matlab-block-end-pre-if, matlab-block-end-pre-no-if): If the tic-toc flag is nil, don't indent tic/tocs. 2008-05-19 Eric Ludlam * semantic-matlab.el: Copied from cedet repository. * matlab.el (matlab-keyword-list): Add mcos keywords (matlab-defun-regex, matlab-block-beg-pre-if, matlab-block-beg-pre-no-if): Updated w/ mcos keywords. * mlint.el (mlint-calculate-cyclic-complexity-flag): New flag. (mlint-flags): Add -fix (mlint-run): Use cyclic-complexity flag. * matlab.el (matlab-mode-version): Update (matlab-cellbreak-face): Fancy new face for cell-breaks. (matlab-font-lock-adjustments): Update cellbreak face. (matlab-font-lock-keywords): Add cellbreak highlighting. (matlab-mode): Fix spelling in indent-sexp keybinding to doc. 2007-03-06 Eric Ludlam * mlint.el: (mlint-clear-warnings, mlint-clear-cross-function-variable-highlighting): Make font-lock optional. 2007-01-08 Eric Ludlam * matlab.el: (matlab-block-end-pre-if, matlab-block-end-pre-no-if): Support assigning toc into a subs-assign. (Thanks Jim Van Zant) 2006-10-04 Eric Ludlam * mlint.el (mlint-output-regex): Support the changed mlint output syntax (mlint-symtab-line-regexp): Support changed mlint table output syntax (mlint-warning-code-alist): Obsolete (mlint-error-fix-alist): Obsolete (mlint-error-id-fix-alist): New version of the old error-fix-alist. (mlint-run): Add the "-edit" flag when highlighting crossfunction variables. Updated parsing of the mlint table for highlighting cross-function variables. Updated mechanism for highlighting the cross-function variables. (mlint-lm-entry): Update warningid doc. (mlint-warning->class, mlint-warningid->class): Name change. Use new table of warning ids instead of parsing warning strings. (linemark-new-entry): Use warning id, not warning string for class determination. (mlint-lm-replace-focus): new-text is no longer class allocated. (mlint-lm-entry-depricated): New class rule. (mlint-lm-entry-isstr, mlint-lm-entry-setstr): Deleted. (mlint-lm-eval->trycatch): Commented out. Not provided by mlint, but it is cool and could be resurrected. (mlint-highlight): Can't provide warningcode anymore. (mlint-clear-cross-function-variable-overlays): Renamed to (mlint-clear-nested-function-info-overlays): updated w/ more info. (mlint-clear-cross-function-variable-highlighting) (mlint-minor-mode): Use new clear function for nested function info. * matlab.el: Make fill-paragraph work around cell headings nicely. Add `matlab-shell-run-cell' for cell-mode style execution of code. Change the page delimiter to include cell breaks. Support "parfor", available in MATLAB 2006a. Treat cell start comments as the start of a comment block, even if comments preceed it. Make sure typing in comment chars moves cursor to the correct location. 2005-12-02 Eric Ludlam * ChangeLog: Build and dependancies changes. * INSTALL, README: Add info about CEDET dependancies. * Makefile, Project.ede, matlab-load.el: Build system. 2005-12-02 Eric Ludlam * README, INSTALL: Refer to CEDET project for dependancies. * Makefile, Project.ede: New build system. * matlab-load.el: autoloads file. 2005-12-01 Eric Ludlam * matlab.el: Removed ChangLog from end of file. matlab-mode/toolbox/0000775000175000017500000000000014611713120015440 5ustar sebastiensebastienmatlab-mode/toolbox/ebstack.m0000664000175000017500000000047114611713120017234 0ustar sebastiensebastienfunction ebstack(FRAMEIDX) % Emacs version of dbstack. Updates Emacs for where we are in the stack. [ST, I] = dbstack('-completenames'); % Send emacs our updated stack es = getappdata(groot, 'EmacsStack'); if nargin == 1 I = FRAMEIDX; end es.updateEmacs(ST, I); end matlab-mode/toolbox/dbhotlink.m0000664000175000017500000000071314611713120017575 0ustar sebastiensebastienfunction dbhotlink() % Display text that EMACS can interpret as a hotlink % so the debugger can auto move to the right spot. % Input L is the stack frame to specify. % If L is not provided, then use the current stack frame. [ST, I] = dbstack('-completenames'); disp('(progn '); es = getappdata(groot, 'EmacsStack'); es.updateForHotLinks(ST, I); bp = getappdata(groot, 'EmacsBreakpoints'); bp.updateForHotLinks(); disp(')'); end matlab-mode/toolbox/emacstipstring.m0000664000175000017500000000031414611713120020650 0ustar sebastiensebastienfunction emacstipstring(expr) % Take EXPR, and convert into a tooltip friendly string. % This utility is mean to be used by emacs to create text to display % whie debugging code. disp(expr) end matlab-mode/toolbox/emacsnetshell.m0000664000175000017500000000372514611713120020454 0ustar sebastiensebastienfunction nso = emacsnetshell(cmd, data) % Create a connection to an EMACS editor server. % % emacsnetshell('init') - Initialize the connection with Emacs. % emacs will send commands to MATLAB with additional connectivity % information. % % emacsnetshell('ack') - Send ack to Emacs, it should echo back. % % emacsnetshell('output',data) - Send DATA as output to display in Emacs. % emacsnetshell('error',data) - Send DATA as error description to Emacs. % emacsnetshell('eval',data) - Send DATA - a string containing an Emacs % lisp form which will be evaluated in Emacs. EMACSSERVER = getappdata(groot, 'EmacsNetShell'); if nargin == 0 if isempty(EMACSSERVER) cmd = 'init'; else cmd = 'fetch'; end end if strcmp(cmd, 'fetch') % Fetch means to get the server and return it. Do not create % the server on fetch - otherwise no way to know if a server was started % or not. nso = EMACSSERVER; return; elseif strcmp(cmd, 'shutdown') % Shutdown our connection to emacs. setappdata(groot, 'EmacsNetShell', []); if ~isempty(EMACSSERVER) delete(EMACSSERVER); end else if ~isempty(EMACSSERVER) && ~isvalid(EMACSSERVER) EMACSSERVER = []; setappdata(groot, 'EmacsNetShell', EMACSSERVER); end if isempty(EMACSSERVER) EMACSSERVER = emacs.EmacsServer(); setappdata(groot, 'EmacsNetShell', EMACSSERVER); sendinit = false; else sendinit = true; end if ~ischar(cmd) error('Command must be a char vector.'); end if ~strcmp(cmd,'init') || sendinit if nargin == 2 EMACSSERVER.SendCommand(cmd, data); else EMACSSERVER.SendCommand(cmd); end end end if nargout == 1 nso = EMACSSERVER; end end matlab-mode/toolbox/ebclear.m0000664000175000017500000000036114611713120017213 0ustar sebastiensebastienfunction ebclear(varargin) % Emacs version of dbstop. Tells emacs which breakpoints are active. dbclear(varargin{:}); % Send emacs some breakpoints bp = getappdata(groot, 'EmacsBreakpoints'); bp.updateEmacs; end matlab-mode/toolbox/Makefile0000664000175000017500000000115114611713120017076 0ustar sebastiensebastien# Automatically Generated Makefile by EDE. # For use with: make # Relative File Name: toolbox/Makefile # # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST. # EDE is the Emacs Development Environment. # http://cedet.sourceforge.net/ede.shtml # top=../ ede_FILES=Project.ede Makefile matlab_MISC=emacsinit.m opentoline.m emacsdocomplete.m dbhotlink.m emacsnetshell.m emacsrunregion.m +emacs/@EmacsServer/EmacsServer.m VERSION=4.0 DISTDIR=$(top)matlab-emacs-$(VERSION)/toolbox all: matlab matlab: @ tags: .PHONY: dist dist: mkdir $(DISTDIR) cp $(matlab_MISC) $(ede_FILES) $(DISTDIR) # End of Makefile matlab-mode/toolbox/emacsdocomplete.m0000664000175000017500000000557014611713120020771 0ustar sebastiensebastienfunction emacsdocomplete(substring) % Ask for completions of SUBSTRING from MATLAB. % This is used by Emacs TAB in matlab-shell to provide possible % completions. This hides the differences between versions % for the calls needed to do completions. % Custom completions for Emacs can be added to MATLAB (*.m) files by: % 1. In a comment, place the string "SUPPORTS_DASH_COMPLETE" % 2. Handle the -complete argument which produces completion strings % of the form: % 'CMD_TEXT_TO_REPLACE' --> 'REPLACEMENT_TEXT' % 'OPTION1' % 'OPTION2' % ... % See details in `matlab-shell-completion-list'. persistent completeSw; % if completeSw(cmd), then supports -complete if isempty(completeSw) completeSw=containers.Map(); end cmd=regexp(substring,'^(\w+)\s+[^\)]','tokens'); if length(cmd)==1 cmd=cmd{1}{1}; if completeSw.isKey(cmd) supportsDashComplete = completeSw(cmd); else supportsDashComplete = false; % assume f=which(cmd); if regexp(f,'\.m$') fid=fopen(f,'r'); if fid ~= -1 while true l = fgetl(fid); if ~ischar(l), break, end if regexp(l,'SUPPORTS_DASH_COMPLETE') supportsDashComplete = true; break end end fclose(fid); end end completeSw(cmd) = supportsDashComplete; end if supportsDashComplete % For /path/to/cmd.ext we have /path/to/cmd.complete which % signals that we can get the completions by calling % CMD -complete ARGS completeCmd = regexprep(substring,'^(\w+)','$1 -complete'); disp('emacs_completions_output ='); evalin('base',completeCmd); return end end v = ver('MATLAB'); if str2double(v.Version) < 8.4 % Pre R2014b: partial_string extracmd = ''; else % Post R2014b: partial_string, caret, num extracmd = [ ', ' num2str(length(substring)) ',0' ]; % DEV NOTE: If you find a test failure, contact Eric Ludlam % to also update matlab-emacs SF repository. end substringQuoted = strrep(substring, '''', ''''''); command = [ 'matlabMCRprocess_emacs = com.mathworks.jmi.MatlabMCR;' ... 'emacs_completions_output = matlabMCRprocess_emacs.mtFindAllTabCompletions(''' ... substringQuoted '''' extracmd '),' ... 'clear(''matlabMCRprocess_emacs'',''emacs_completions_output'');' ]; % Completion engine needs to run in the base workspace to know % what the variables you have to work with are. evalin('base',command); end matlab-mode/toolbox/opentoline.m0000664000175000017500000000164114611713120017774 0ustar sebastiensebastienfunction opentoline(file, line, column) %OPENTOLINE Open to specified line in function file in Emacs. % This is a hack to override the built-in opentoline program in MATLAB. % % Remove this M file from your path to get the old behavior. editor = system_dependent('getpref', 'EditorOtherEditor'); editor = editor(2:end); if nargin==3 linecol = sprintf('+%d:%d',line,column); else linecol = sprintf('+%d',line); end f = which(file); if ~isempty(f) file=f; end if ispc % On Windows, we need to wrap the editor command in double quotes % in case it contains spaces system(['"' editor '" "' linecol '" "' file '"&']); else % On UNIX, we don't want to use quotes in case the user's editor % command contains arguments (like "xterm -e vi") system([editor ' "' linecol '" "' file '" &']); end end matlab-mode/toolbox/emacscd.m0000664000175000017500000000133014611713120017212 0ustar sebastiensebastienfunction emacscd(dir) % CD to DIRECTORY in a way that Emacs Shell won't see via dirtrack. % Instead, show example of how to tell Emacs what the new directory is. if nargin == 1 cd(dir); end emacs = getenv('INSIDE_EMACS'); if isempty(emacs) disp('Not inside Emacs') else % disp('Inside Emacs - Sending directory cookie.') % Set matlab-shell `default-directory' to the current MATLAB pwd. Note, default-directory % requires a trailing slash so things like `find-file' C-x C-f work as expect. disp('(eval)') disp(['(setq default-directory "' pwd '/")']) disp('') end end % LocalWords: dirtrack EMACSCAP setq matlab-mode/toolbox/Project.ede0000664000175000017500000000114214611713120017523 0ustar sebastiensebastien;; Object ede-proj-project-15781502eb88 ;; EDE Project Files are auto generated: Do Not Edit (ede-proj-project "ede-proj-project" :file "Project.ede" :name "toolbox" :targets (list (ede-proj-target-makefile-miscelaneous "ede-proj-target-makefile-miscelaneous" :name "matlab" :path "" :source '("emacsinit.m" "opentoline.m" "emacsdocomplete.m" "dbhotlink.m" "emacsnetshell.m" "emacsrunregion.m" "+emacs/@EmacsServer/EmacsServer.m" "help.m" "emacscd.m" "dbstop.m" "emacss_dbstop.m" "ebstatus.m" "ebstack.m" "emacspathinit.m" "ebstop.m" "ebquit.m" "ebcont.m" "ebstep.m" "ebup.m")))) matlab-mode/toolbox/emacsrunregion.m0000664000175000017500000000171314611713120020641 0ustar sebastiensebastienfunction emacsrunregion(file, startchar, endchar) % Run code from FILE between STARTCHAR and ENDCHAR. % Command sent by Emacs for run-cell & run-region functionality. % Filter out emacs tramp file path prefix trampMatch = regexp(file, {'/*:',':/'}); if (~isempty(trampMatch{1})) file = file((trampMatch{2}+1):end); end if ~exist(file,'file') error('You must save your region into a file accessible by MATLAB process.'); end % Now figure out if shortFileName is on the path. [ fullFilePath, shortFileName ] = fileparts(file); onpath = ~isempty(which(shortFileName)); % 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 txt = fileread(file); evalTxt = txt(startchar:min(endchar,length(txt))); evalin('base',evalTxt); end matlab-mode/toolbox/+emacs/0000775000175000017500000000000014611713120016603 5ustar sebastiensebastienmatlab-mode/toolbox/+emacs/@Breakpoints/0000775000175000017500000000000014611713120021164 5ustar sebastiensebastienmatlab-mode/toolbox/+emacs/@Breakpoints/Breakpoints.m0000664000175000017500000001271214611713120023626 0ustar sebastiensebastienclassdef Breakpoints < handle % Class BREAKPOINTS - manages breakpoints that are shared with Emacs. properties % Breakpoints we have sent to Emacs already. EmacsBreakpoints = []; % Netshell object if we should direct that way instead. NetShellObject = []; end methods function bp = Breakpoints(nso) if nargin == 1 bp.NetShellObject = nso; end bp.resetEmacs(); end function str = updateString(bp, show) % update Emacs with just the breakpoint deltas. % if there is a delta, display prefix text before sending % our update. if nargin < 2 show = false; end currpts = unwindBreakpoints(builtin('dbstatus')); oldpts = bp.EmacsBreakpoints; addpts = []; delpts = []; if isempty(oldpts) addpts = currpts; else % Did we add any new breakpoints? for i=1:length(currpts) if ~isempty(currpts(i).name) if ~ptInList(currpts(i).name, currpts(i).line, oldpts) if isempty(addpts) addpts = currpts(i); else addpts(end+1) = currpts(i); %#ok end end else % This is for dbstop if error and friends. end end % Did we remove any old breakpoints; for i=1:length(oldpts) if ~ptInList(oldpts(i).name, oldpts(i).line, currpts) if isempty(delpts) delpts = oldpts(i); else delpts(end+1) = oldpts(i); %#ok end end end end if show showcmd = [ newline '(mlg-show-breakpoints)']; elseif ~isempty(delpts) || ~isempty(addpts) showcmd = [ newline '(mlg-refresh-breakpoint-buffer)']; else showcmd = ''; end bp.EmacsBreakpoints = currpts; if ~isempty(delpts) || ~isempty(addpts) || ~isempty(showcmd) str = [ '(progn ;;breakpoint' newline ... sendPtList('del', delpts) ... sendPtList('add', addpts) ... showcmd ... ')' ]; else str = ''; end end function updateEmacs(bp, show) % update Emacs with just the breakpoint deltas. % if there is a delta, display prefix text before sending % our update. if nargin < 2 show = false; end str = bp.updateString(show); if ~isempty(str) % Send the sequence of Emacs commands to update breakpoints if isempty(bp.NetShellObject) disp(['(eval)' newline]); disp(str) disp([newline '']) else bp.NetShellObject.SendEval(str); end end end function updateForHotLinks(bp) str = bp.updateString(false); if ~isempty(str) disp(str) end end function resetEmacs(bp) currpts = unwindBreakpoints(builtin('dbstatus')); bp.EmacsBreakpoints = currpts; str = ['(progn (mlg-reset-breakpoints)' newline ... sendPtList('add', currpts) ')']; if isempty(bp.NetShellObject) disp(['(eval)' newline ]); disp(str); disp([newline '']) else bp.NetShellObject.SendEval(str); end end end end function str = sendPtList(ad, bpstructlist) str = ''; for i=1:length(bpstructlist) str = [ str '(mlg-' ad '-breakpoint "' fixFile(bpstructlist(i).file) '" "' ... bpstructlist(i).name '" ' ... num2str(bpstructlist(i).line) ')' newline]; %#ok end end function newlist = unwindBreakpoints(bplist) % Unwind the breakpoints in BPLIST so there is one struct per line per file. newlist = []; for f = 1:length(bplist) for l = 1:length(bplist(f).line) news.name = bplist(f).name; news.file = bplist(f).file; news.line = bplist(f).line(l); if isempty(newlist) newlist = news; else newlist(end+1) = news; %#ok end end end end function tf = ptInList(name, line, lst) % Return true if a breakpoint at NAME/LINE exists in LST. tf = false; for i=1:length(lst) tf = strcmp(name, lst(i).name) && line==lst(i).line; if tf, return; end end end function nf = fixFile(filename) % Fix FILENAME so it has no escape chars, that way we can send to Emacs. nf = regexprep(filename,"\", "/"); endmatlab-mode/toolbox/+emacs/@EmacsServer/0000775000175000017500000000000014611713120021122 5ustar sebastiensebastienmatlab-mode/toolbox/+emacs/@EmacsServer/EmacsServer.m0000664000175000017500000001260514611713120023523 0ustar sebastiensebastienclassdef EmacsServer < handle % Class EMACSSERVER - Support a TCP connection to an Emacs server. % Collects input from Emacs, and runs commands. % Sends commands to Emacs from MATLAB if needed. % properties % True to send Emacs Stack info as the user steps through the debugger. FollowStack = false; end properties (Access='protected') tcpclient; timer; end methods function ES = EmacsServer() % Construct the Emacs Server. Create the TCP client and ES.tcpclient = tcpclient('localhost', 32475); if isempty(ES.tcpclient) delete(ES); error('Unable to connect to Emacs.'); end ES.timer = timer('Name','Emacs NetShell timer', ... 'TimerFcn', {@watch_emacs ES}, ... 'Period', 3,... 'BusyMode', 'drop',... 'ErrorFcn', {@drop_emacs ES},... 'ExecutionMode', 'fixedSpacing'); start(ES.timer); ES.SendCommand('init'); end function delete(ES) try stop(ES.timer); end delete(ES.tcpclient); delete(ES.timer); end function SendCommand(ES, cmd, data) % Commands have 2 parts, the COMMAND and DATA. % COMMAND is sent to emacs followed by a newline. This should be a single % word. SendCommand adds the newline. % DATA can be any string. % The full command is terminated by a NULL -> uint8(0) write(ES.tcpclient, uint8([ cmd newline])); if nargin > 2 && ~isempty(data) write(ES.tcpclient, uint8(data)); end write(ES.tcpclient, uint8(0)); end function SendEval(ES, lispform) % Send the LISPFFORM for Emacs to evaluate. ES.SendCommand('eval', lispform); end end properties (Access='protected') accumulator = ''; end methods (Access='protected') function ReadCommand(ES) msg = char(read(ES.tcpclient)); ES.accumulator = [ ES.accumulator msg ]; while ~isempty(ES.accumulator) k = strfind(ES.accumulator, char(0)); if isempty(k) % No complete commands. Exit. disp(['partial accumulation: ' ES.accumulator]); return; end datamsg = ES.accumulator(1:k(1)-1); ES.accumulator = ES.accumulator(k(1)+1:end); % Now peal the datamsg into it's constituant parts. cr = strfind(datamsg, newline); if isempty(cr) cmd = datamsg; data = ''; else cmd = datamsg(1:cr(1)-1); data = datamsg(cr(1)+1:end); end ES.ExecuteRemoteCommand(cmd, data); end end function ExecuteRemoteCommand(ES, cmd, data) % When we recieve a command from Emacs, eval it. switch cmd case 'nowledge' disp('Acknowledgement recieved.'); case 'ack' disp('Ack Recieved. Sending ack back.'); ES.SendCommand('nowledge'); case 'eval' try disp(['>> ' data]); evalin('base',data); catch ERR disp(ERR.message); ES.SendCommand('error', ERR.message); end case 'evalc' disp('Evalc request.'); try OUT = evalc(data); catch ERR OUT = ERR.message; end if ~isempty(OUT) ES.SendCommand('output',uint8(OUT)); else disp('No output'); end otherwise disp('Unknown command from Emacs'); end end end end function watch_emacs(~, ~, ES) % Timer Callback Function: % Watch for bytes available from the Emacs network connection, and act on any events. ba = ES.tcpclient.BytesAvailable; if ba > 0 ES.ReadCommand(); else % Nothing recieved %disp('No Luv from Emacs'); % Check if we are still alive. We can only do that with a % write- so send an empty message. try write(ES.tcpclient, 0); catch disp('Connection to Emacs lost. Shutting down net server'); delete(ES); end end if ES.FollowStack es = getappdata(groot, 'EmacsStack'); [ST, I] = dbstack('-completenames'); es.updateEmacs(ST, I); end end function drop_emacs(~, ~, ES) % If the timer throws an error, then shutdown. delete(ES); disp('Error in timer, dropping connection to Emacs.'); endmatlab-mode/toolbox/+emacs/@Stack/0000775000175000017500000000000014611713120017750 5ustar sebastiensebastienmatlab-mode/toolbox/+emacs/@Stack/Stack.m0000664000175000017500000001057014611713120021176 0ustar sebastiensebastienclassdef Stack < handle % Class STACK - Manage Emacs' stack state. properties % Stack last sent to Emacs EmacsStack = []; EmacsFrame = 1; % Netshell object if we should direct that way instead. NetShellObject = []; % Flag - do we need to tell Emacs what changed? StackPending = false; FramePending = false; end methods function es = Stack(nso) if nargin == 1 es.NetShellObject = nso; end es.resetEmacs(); end function captureStack(es, newstack, newframe) if ~isempty(newstack) && ... ( strcmp(newstack(1).name, 'ebstack') ||... strcmp(newstack(1).name, 'dbhotlink') ) newstack = newstack(2:end); end idx = 1; cutbelow = 0; while idx < length(newstack) if contains(newstack(idx).name, 'timercb') % As soon as we see Emacs Server, then we went too far. cutbelow = idx; end idx = idx+1; end if cutbelow newstack = newstack(cutbelow+1:end); end if ~stackEqual(es.EmacsStack, newstack) es.EmacsStack = newstack; es.StackPending = true; end if newframe ~= es.EmacsFrame es.EmacsFrame = newframe; es.FramePending = true; end end function updateEmacs(es, newstack, newframe) % Update Emacs' view of the stack es.captureStack(newstack, newframe); str = [ '(progn ;;Stack' newline ]; if es.StackPending str = [ str ... stackFrames(es.EmacsStack) ... newline ]; end str = [ str ... ' (mlg-set-stack-frame ' num2str(es.EmacsFrame) ')' ]; str = [ str ')']; es.StackPending = false; es.FramePending = false; if isempty(es.NetShellObject) disp('(eval)'); disp(str); disp('') else es.NetShellObject.SendEval(str); end end function resetEmacs(es) end function updateForHotLinks(es, newstack, newframe) es.captureStack(newstack, newframe); % updateEmacs(es, newstack, newframe); if es.StackPending || es.FramePending str = [ '(progn ;;Stack' newline ]; if es.StackPending str = [ str ... stackFrames(es.EmacsStack) ... newline ]; end str = [ str ... ' (mlg-set-stack-frame-via-gud ' num2str(es.EmacsFrame) ')' ]; str = [ str ')']; else str = ''; end es.StackPending = false; es.FramePending = false; if ~isempty(str) if isempty(es.NetShellObject) disp(str); else es.NetShellObject.SendEval(str); end end end end end function str=stackFrames(ST) % Return Emacs Lisp form representing the current stack. str = ' (mlg-set-stack (quote ('; for i=1:length(ST) str = [str newline ' ("' fixFile(ST(i).file) '" "' ... ST(i).name '" ' num2str(ST(i).line) ')' ]; %#ok end str = [ str ')))' ]; end function nf = fixFile(filename) % Fix FILENAME so it has no escape chars, that way we can send to Emacs. nf = regexprep(filename,"\", "/"); end function thesame = stackEqual(stack1, stack2) thesame = true; if length(stack1) ~= length(stack2) thesame=false; return; end for i=1:length(stack1) if ~strcmp(stack1(i).name, stack2(i).name) || ... stack1(i).line ~= stack2(i).line thesame = false; return end end endmatlab-mode/toolbox/+emacs/set.m0000664000175000017500000000572614611713120017566 0ustar sebastiensebastienfunction set(varargin) % Setup an Emacs option based on Name/Value pairs. % Valid options include: % % netshell - Initialize a netshell connection. % clientcmd - What to use for `edit' client command P = inputParser; addParameter(P, 'netshell', 0, @isnumeric) addParameter(P, 'clientcmd', "", @ischar) addParameter(P, 'followstack', -1, @isnumeric) parse(P, varargin{:}); clientcommand = P.Results.clientcmd; netshellport = P.Results.netshell; followstack = P.Results.followstack; %% Client Command if ~isempty(clientcommand) if usejava('jvm') % Use clientcommand (e.g. emacsclient -n) for text editing if verLessThan('MATLAB','9.4') % Before settings API was introduced com.mathworks.services.Prefs.setBooleanPref('EditorBuiltinEditor',false); else s = settings; s.matlab.editor.UseMATLABEditor.TemporaryValue = 0; end if verLessThan('MATLAB','9.9') % Before OtherEditor was read from settings API com.mathworks.services.Prefs.setStringPref('EditorOtherEditor', clientcommand); else s = settings; s.matlab.editor.OtherEditor.TemporaryValue = clientcommand; end end if isunix % In some cases 'edit foo.m' requires EDITOR to be set so we see foo.m in Emacs. % - Consider 'emacs -nw' which and running without Java on Linux. % - On Mac EDITOR is always used, but it must be a single executable w/o switches % - Any system('command') that uses opens files should open in Emacs. setenv('EDITOR_EMACSCLIENT', clientcommand); p1 = fileparts(mfilename('fullpath')); % p1 /path/to/matlab-emacs/toolbox/+emacs p2 = fileparts(p1); % p2 == /path/to/matlab-emacs/toolbox/+emacs p3 = fileparts(p2); % p3 = /path/to/matlab-emacs/ % We cannot run using the full path to matlab-emacsclient.sh because % checkMacApp(applicationName,'emacs') will fail if the path contains the string % "/emacs", e.g. /path/to/emacs/packages/matlab-emacs. Therefore, update PATH to make % matlab-emacsclient.sh available. setenv('EDITOR', 'matlab-emacsclient.sh'); setenv('PATH',[getenv('PATH'),':',p3,'/bin']); end end %% Netshell Support if netshellport nso = emacsnetshell('init'); % Assign netshell into our reporter objects. bp = getappdata(groot, 'EmacsBreakpoints'); bp.NetShellObject = nso; st = getappdata(groot, 'EmacsStack'); st.NetShellObject = nso; end %% Follow Stack settings if followstack == 0 || followstack == 1 EMACSSERVER = getappdata(groot, 'EmacsNetShell'); EMACSSERVER.FollowStack = followstack; end end % LocalWords: netshell clientcmd followstack clientcommand emacsclient nw matlab-mode/toolbox/ebstatus.m0000664000175000017500000000020114611713120017441 0ustar sebastiensebastienfunction ebstatus % Send emacs some breakpoints bp = getappdata(groot, 'EmacsBreakpoints'); bp.updateEmacs(true); end matlab-mode/toolbox/emacsrun.m0000664000175000017500000000146214611713120017436 0ustar sebastiensebastienfunction 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); endmatlab-mode/toolbox/emacsinit.m0000664000175000017500000000571214611713120017577 0ustar sebastiensebastienfunction emacsinit() % EMACSINIT Initialize the current MATLAB session for matlab-shell-mode % me = mfilename('fullpath'); % This command can also update the path for the emacs Toolbox directory. % This will make it possible to use this one command from a standalone % MATLAB and setup netshell. [ myDir ] = fileparts(me); if ~contains(path, myDir) disp(['Updating MATLAB Path to support Emacs toolbox: addpath(' myDir ')']); addpath(myDir,'-begin'); rehash; end if usejava('jvm') %{ % Leaving in old hot-link code and description (see below) % in case someone with older MATLAB's need to use this. v = ver('MATLAB'); if str2double(v.Version) < 8.5 % In 8.5 (R2015b) the MATLAB removed the ability to display hot link's when % debugging in -nodesktop mode. In R2015a (8.4) MATLAB would display in -nodesktop % mode something like: % >> dbstop in testit % >> testit % >> 3 a = 10 * 10; % K>> % and emacs then would use the hot link to drive debugging from within emacs. % Given that R2015b and later does not have hot links, we use the graphical debugging % by leaving EditorGraphicalDebugging set to true. % Use the desktop hotlinking system in MATLAB Shell. matlab-shell % will interpret them, and provide clickable areas. % NOTE: This doesn't work in all cases where HotLinks are used. feature('HotLinks','on'); % Disable built-in editor showing up for debugging com.mathworks.services.Prefs.setBooleanPref('EditorGraphicalDebugging', false); end %} % Starting in matlab-emacs v 4.0, we can simulate debugging % hot links, so we should always disable graphical % debugging. % Disable built-in editor showing up for debugging com.mathworks.services.Prefs.setBooleanPref('EditorGraphicalDebugging', false); % Disable wrapping of text lines. Emacs will wrap or not based on user preference. com.mathworks.services.Prefs.setBooleanPref('WrapLines',false) end % Check if we're running inside emacs. If we are NOT, then force the enablement of % the netshell interface to Emacs. emacs_env = getenv('INSIDE_EMACS'); if isempty(emacs_env) startnetshell = true; else startnetshell = false; end % If requested, start the Emacs netshell interface. if startnetshell nso = emacsnetshell('init'); else nso = []; end % Initialize Emacs breakpoint handler. bp = emacs.Breakpoints(nso); setappdata(groot, 'EmacsBreakpoints', bp); % Initialize Emacs stack handler. st = emacs.Stack(nso); setappdata(groot, 'EmacsStack', st); end % LocalWords: netshell nodesktop testit hotlinking matlab-mode/toolbox/help.m0000664000175000017500000000326614611713120016555 0ustar sebastiensebastienfunction [out, docTopic] = help(varargin) % Provide help, augmented so Emacs picks it up to display in a special buffer. % See the help for the built-in help command by asking for "help help" in % MATLAB, which will redirect to the correct location. origPath = path; cleanup = onCleanup(@()path(origPath)); me = mfilename('fullpath'); % Recursion Detection. (Not sure why we sometimes recurse in the first call to help.) [ST] = dbstack('-completenames',1); files = { ST.file }; mask = strncmp(files, me, length(me)); if any(mask) disp('MATLAB Emacs help override recursion detected. Exiting.'); return; end % Remove this dir from path so we can get to the built-in version of help. myDir = fileparts(me); rmpath(myDir); builtinHelp = which('help'); clear cleanup; helpPath = fileparts(builtinHelp); % Cd to where built-in help is so we call that first. On cleanup restore % old working directory. oldCWD = pwd; cd(helpPath); cleanup = onCleanup(@()cd(oldCWD)); args = varargin; nso = emacsnetshell('fetch'); if isempty(nso) cookie = true; else cookie = false; end if nargin > 0 && strcmp(args{1}, '-emacs') cookie=false; args = args(2:end); end switch nargout case 0 if cookie disp(['(*MATLAB Help: ' args{:} '*)']); end try help(args{:}); catch ERR disp(ERR) end if cookie disp(''); end case 1 [out] = help(args{:}); case 2 [out, docTopic] = help(args{:}); end endmatlab-mode/toolbox/ebstop.m0000664000175000017500000000035714611713120017117 0ustar sebastiensebastienfunction ebstop(varargin) % Emacs version of dbstop. Tells emacs which breakpoints are active. dbstop(varargin{:}); % Send emacs some breakpoints bp = getappdata(groot, 'EmacsBreakpoints'); bp.updateEmacs; end matlab-mode/matlab-syntax.el0000664000175000017500000005117414611713120017070 0ustar sebastiensebastien;;; matlab-syntax.el --- Manage MATLAB syntax tables and buffer parsing. ;; ;; Copyright (C) 2021 Eric Ludlam ;; ;; Author: ;; ;; 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 '(1 . nil) ;; 3 = symbol, 1 = punctuation "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 matlab-mode/linemark.el0000664000175000017500000003727614625574731016137 0ustar sebastiensebastien;;; linemark.el --- Manage groups of lines with marks. ;; ;; Author: Eric M. Ludlam ;; Maintainer: Eric M. Ludlam ;; Created: Dec 1999 ;; Keywords: lisp ;; ;; Copyright (C) 1999-2020 Eric M. Ludlam ;; ;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;; ;;; Commentary: ;; ;; This is a library of routines which help Lisp programmers manage ;; groups of marked lines. Common uses for marked lines are debugger ;; breakpoints and watchpoints, or temporary line highlighting. It ;; could also be used to select elements from a list. ;; ;; The reason this tool is useful is because cross-emacs overlay ;; management can be a pain, and overlays are certainly needed for use ;; with font-lock. (eval-and-compile (require 'matlab-compat)) (eval-when-compile (require 'cl)) ;;; Code: ;; Compatibility (eval-and-compile ;; `object-name-string' is an obsolete function (as of 24.4); use `eieio-object-name-string' instead. (cond ((fboundp 'eieio-object-name-string) (defalias 'linemark-object-name-string 'eieio-object-name-string)) (t (defalias 'linemark-object-name-string 'object-name-string))) ) (defgroup linemark nil "Line marking/highlighting." :group 'tools ) (eval-and-compile ;; These faces need to exist to show up as valid default ;; entries in the classes defined below. (defface linemark-stop-face '((((class color) (background light)) (:background "#ff8888")) (((class color) (background dark)) (:background "red3"))) "*Face used to indicate a STOP type line." :group 'linemark) (defface linemark-caution-face '((((class color) (background light)) (:background "yellow")) (((class color) (background dark)) (:background "yellow4"))) "*Face used to indicate a CAUTION type line." :group 'linemark) (defface linemark-go-face '((((class color) (background light)) (:background "#88ff88")) (((class color) (background dark)) (:background "green4"))) "*Face used to indicate a GO, or OK type line." :group 'linemark) (defface linemark-funny-face '((((class color) (background light)) (:background "cyan")) (((class color) (background dark)) (:background "blue3"))) "*Face used for elements with no particular criticality." :group 'linemark) ) (defclass linemark-entry () ((filename :initarg :filename :type string :documentation "File name for this mark.") (line :initarg :line :type number :documentation "Line number where the mark is.") (face :initarg :face :initform 'linemark-caution-face :documentation "The face to use for display.") (parent :documentation "The parent `linemark-group' containing this." :type linemark-group) (overlay :documentation "Overlay created to show this mark." :type (or overlay null) :initform nil :protection protected)) "Track a file/line associations with overlays used for display.") (defclass linemark-group () ((marks :initarg :marks :type list :initform nil :documentation "List of `linemark-entries'.") (face :initarg :face :initform 'linemark-funny-face :documentation "Default face used to create new `linemark-entries'.") (active :initarg :active :type boolean :initform t :documentation "Track if these marks are active or not.")) "Track a common group of `linemark-entries'.") ;;; Functions ;; (defvar linemark-groups nil "List of groups we need to track.") (defun linemark-create-group (name &optional defaultface) "*Obsolete*. Create a group object for tracking linemark entries. Do not permit multiple groups with the same NAME. Optional argument DEFAULTFACE is the :face slot for the object." (linemark-new-group 'linemark-group name :face defaultface) ) (defun linemark-new-group (class name &rest args) "Create a new linemark group based on the linemark CLASS. Give this group NAME. ARGS are slot/value pairs for the new instantiation." (let ((newgroup nil) (foundgroup nil) (lmg linemark-groups)) ;; Find an old group. (while (and (not foundgroup) lmg) (if (string= name (linemark-object-name-string (car lmg))) (setq foundgroup (car lmg))) (setq lmg (cdr lmg))) ;; Which group to use. (if foundgroup ;; Recycle the old group (setq newgroup foundgroup) ;; Create a new group (setq newgroup (apply 'make-instance class name args)) (setq linemark-groups (cons newgroup linemark-groups))) ;; Return the group newgroup)) (defun linemark-at-point (&optional pos group) "Return the current variable `linemark-entry' at point. Optional POS is the position to check which defaults to point. If GROUP, then make sure it also belongs to GROUP." (if (not pos) (setq pos (point))) (let ((o (overlays-at pos)) (found nil)) (while (and o (not found)) (let ((og (overlay-get (car o) 'obj))) (if (and og (linemark-entry-child-p og)) (progn (setq found og) (if group (if (not (eq group (oref found parent))) (setq found nil))))) (setq o (cdr o)))) found)) (defun linemark-next-in-buffer (group &optional arg wrap) "Return the next mark in this buffer belonging to GROUP. If ARG, then find that manu marks forward or backward. Optional WRAP argument indicates that we should wrap around the end of the buffer." (if (not arg) (setq arg 1)) ;; default is one forward (let* ((entry (linemark-at-point (point) group)) (nc (if entry (if (< 0 arg) (linemark-end entry) (linemark-begin entry)) (point))) (dir (if (< 0 arg) 1 -1)) (ofun (if (> 0 arg) 'previous-overlay-change 'next-overlay-change)) (bounds (if (< 0 arg) (point-min) (point-max))) ) (setq entry nil) (catch 'moose (save-excursion (while (and (not entry) (/= arg 0)) (setq nc (funcall ofun nc)) (setq entry (linemark-at-point nc group)) (if (not entry) (if (or (= nc (point-min)) (= nc (point-max))) (if (not wrap) (throw 'moose t) (setq wrap nil ;; only wrap once nc bounds)))) ;; Ok, now decrement arg, and keep going. (if entry (setq arg (- arg dir) nc (linemark-end entry)))))) entry)) ;;; Methods that make things go ;; (cl-defmethod linemark-add-entry ((g linemark-group) &rest args) "Add a `linemark-entry' to G. It will be at location specified by :filename and :line, and :face which are property list entries in ARGS. Call the new entrie's activate method." (let ((file (plist-get args :filename)) (line (plist-get args :line)) (face (plist-get args :face))) (if (not file) (progn (setq file (buffer-file-name)) (if file (setq file (expand-file-name file)) (setq file (buffer-name))))) (when (not line) (setq line (count-lines (point-min) (point))) (if (bolp) (setq line (1+ line)))) (setq args (plist-put args :filename file)) (setq args (plist-put args :line line)) (let ((new-entry (apply 'linemark-new-entry g args))) (oset new-entry parent g) (oset new-entry face (or face (oref g face))) (oset g marks (cons new-entry (oref g marks))) (if (oref g active) (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) )) (cl-defmethod linemark-new-entry ((g linemark-group) &rest args) "Create a new entry for G using init ARGS." (let ((f (plist-get args :filename)) (l (plist-get args :line))) (apply 'linemark-entry (format "%s %d" f l) args))) (cl-defmethod linemark-display ((g linemark-group) active-p) "Set object G to be active or inactive." (mapc (lambda (g) (linemark-display g active-p)) (oref g marks)) (oset g active active-p)) (cl-defmethod linemark-display ((e linemark-entry) active-p) "Set object E to be active or inactive." (if active-p (with-slots ((file filename)) e (if (oref e overlay) ;; Already active nil (let ((buffer)) (if (get-file-buffer file) (setq buffer (get-file-buffer file)) (setq buffer (get-buffer file))) (if buffer (with-current-buffer buffer (save-excursion (goto-char (point-min)) (forward-line (1- (oref e line))) (oset e overlay (make-overlay (point) (save-excursion (end-of-line) (point)) (current-buffer))) (with-slots (overlay) e (overlay-put overlay 'face (oref e face)) (overlay-put overlay 'obj e) (overlay-put overlay 'tag 'linemark)))))))) ;; Not active (with-slots (overlay) e (if overlay (progn (condition-case nil ;; During development of linemark programs, this is helpful (delete-overlay overlay) (error nil)) (oset e overlay nil)))))) (cl-defmethod linemark-delete ((g linemark-group)) "Remove group G from linemark tracking." (mapc 'linemark-delete (oref g marks)) (setq linemark-groups (delete g linemark-groups))) (cl-defmethod linemark-delete ((e linemark-entry)) "Remove entry E from it's parent group." (with-slots (parent) e (oset parent marks (delq e (oref parent marks))) (linemark-display e nil))) (cl-defmethod linemark-begin ((e linemark-entry)) "Position at the start of the entry E." (with-slots (overlay) e (overlay-start overlay))) (cl-defmethod linemark-end ((e linemark-entry)) "Position at the end of the entry E." (with-slots (overlay) e (overlay-end overlay))) ;;; Trans buffer tracking ;; ;; This section sets up a find-file-hook and a kill-buffer-hook ;; so that marks that aren't displayed (because the buffer doesn't ;; exist) are displayed when said buffer appears, and that overlays ;; are removed when the buffer buffer goes away. (defun linemark-find-file-hook () "Activate all marks which can benifit from this new buffer." (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." (let ((o (overlays-in (point-min) (point-max))) (to nil)) (while o (setq to (overlay-get (car o) 'obj)) (if (and to (linemark-entry-child-p to)) (linemark-display to nil)) (setq o (cdr o))))) (add-hook 'find-file-hook 'linemark-find-file-hook) (add-hook 'kill-buffer-hook 'linemark-kill-buffer-hook) ;;; Demo mark tool: Emulate MS Visual Studio bookmarks ;; (defvar viss-bookmark-group (linemark-new-group 'linemark-group "viss") "The VISS bookmark group object.") (defun viss-bookmark-toggle () "Toggle a bookmark on the current line." (interactive) (let ((ce (linemark-at-point (point) viss-bookmark-group))) (if ce (linemark-delete ce) (linemark-add-entry viss-bookmark-group)))) (defun viss-bookmark-next-buffer () "Move to the next bookmark in this buffer." (interactive) (let ((n (linemark-next-in-buffer viss-bookmark-group 1 t))) (if n (progn (goto-char (point-min)) (forward-line (1- (oref n line)))) (ding)))) (defun viss-bookmark-prev-buffer () "Move to the next bookmark in this buffer." (interactive) (let ((n (linemark-next-in-buffer viss-bookmark-group -1 t))) (if n (progn (goto-char (point-min)) (forward-line (1- (oref n line)))) (ding)))) (defun viss-bookmark-clear-all-buffer () "Clear all bookmarks in this buffer." (interactive) (mapcar (lambda (e) (if (or (string= (oref e filename) (buffer-file-name)) (string= (oref e filename) (buffer-name))) (linemark-delete e))) (oref viss-bookmark-group marks))) ;; These functions only sort of worked and were not really useful to me. ;; ;;(defun viss-bookmark-next () ;; "Move to the next bookmark." ;; (interactive) ;; (let ((c (linemark-at-point (point) viss-bookmark-group)) ;; (n nil)) ;; (if c ;; (let ((n (member c (oref viss-bookmark-group marks)))) ;; (if n (setq n (car (cdr n))) ;; (setq n (car (oref viss-bookmark-group marks)))) ;; (if n (goto-line (oref n line)) (ding))) ;; ;; if no current mark, then just find a local one. ;; (viss-bookmark-next-buffer)))) ;; ;;(defun viss-bookmark-prev () ;; "Move to the next bookmark." ;; (interactive) ;; (let ((c (linemark-at-point (point) viss-bookmark-group)) ;; (n nil)) ;; (if c ;; (let* ((marks (oref viss-bookmark-group marks)) ;; (n (member c marks))) ;; (if n ;; (setq n (- (- (length marks) (length n)) 1)) ;; (setq n (car marks))) ;; (if n (goto-line (oref n line)) (ding))) ;; ;; if no current mark, then just find a local one. ;; (viss-bookmark-prev-buffer)))) ;; ;;(defun viss-bookmark-clear-all () ;; "Clear all viss bookmarks." ;; (interactive) ;; (mapcar (lambda (e) (linemark-delete e)) ;; (oref viss-bookmark-group marks))) ;; ;;;###autoload (defun enable-visual-studio-bookmarks () "Bind the viss bookmark functions to F2 related keys. \\ \\[viss-bookmark-toggle] - To=ggle a bookmark on this line. \\[viss-bookmark-next-buffer] - Move to the next bookmark. \\[viss-bookmark-prev-buffer] - Move to the previous bookmark. \\[viss-bookmark-clear-all-buffer] - Clear all bookmarks." (interactive) (define-key global-map [(f2)] 'viss-bookmark-toggle) (define-key global-map [(shift f2)] 'viss-bookmark-prev-buffer) (define-key global-map [(control f2)] 'viss-bookmark-next-buffer) (define-key global-map [(control shift f2)] 'viss-bookmark-clear-all-buffer) ) (provide 'linemark) ;;; linemark.el ends here matlab-mode/Project.ede0000664000175000017500000000227114611713120016041 0ustar sebastiensebastien;; Object ede-proj-project ;; EDE Project Files are auto generated: Do Not Edit (ede-proj-project "ede-proj-project" :file "Project.ede" :name "matlab-emacs" :version "4.0" :targets (list (ede-proj-target-elisp-autoloads "ede-proj-target-elisp-autoloads" :name "autoloads" :path "" :autoload-file "matlab-load.el") (ede-proj-target-makefile-miscelaneous "ede-proj-target-makefile-miscelaneous" :name "misc" :path "" :source '("ChangeLog" "ChangeLog.old1" "ChangeLog.old2" "INSTALL" "README" "dl_emacs_support.m")) (ede-proj-target-elisp "ede-proj-target-elisp" :name "lisp" :path "" :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" :name "cedet" :path "" :source '("semantic-matlab.el" "semanticdb-matlab.el" "srecode-matlab.el" "cedet-matlab.el" "company-matlab-shell.el")))) matlab-mode/tlc.el0000664000175000017500000004707014611713120015066 0ustar sebastiensebastien;;; tlc --- Major mode for editing tlc files ;; ;; Author: Eric M. Ludlam ;; Keywords: tlc ;; X-Abstract: Major mode for editing tlc files (defvar tlc-version "1.3" "The current version of TLC mode.") ;; ;; Copyright 1997-2021 Eric Ludlam ;; ;; This program is derived from 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 2, 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;; ;;; Commentary: ;; ;; This is a major mode for editing Target Language Compiler, TLC, programs. ;; TLC programs are an advanced template system which are used to create ;; source code for compilers, e.g. to produce C code or C++ code. ;; ;; TLC programs consist of : ;; - Keywords of the form %function, %if, etc. ;; - Built-in functions of form EXISTS(). ;; - Statement continuation using "...", e.g. ;; %assign a = b + ... ;; c ;; - Single line comments are of the from: %% line ;; - "%%{", "%%{{", "%%}", "%%}}", etc. 'indent comment shift operators' adjust indentation ;; by 4, 8, -4, -8, etc. ;; - "%%{N}" 'indent comment shift operators' adjust what follows to indent to column N ;; - Block comments are of form: /% ... %/ ;; - All other content is sent to the output to create target language source code, e.g. C. ;; ;; TLC programs therefore mix TLC semantics with target language semantics, e.g. C code: ;; ;; %function Foo(mode) output ;; %if mode.loop ;; int i; ;; for (i = 0; i < %; i++) { ;; %[i] = %[i]; ;; } ;; %else ;; % = %; ;; %endif ;; %endfunction ;; ;; The mix of TLC code and C code can result in challenges for semantic indentation. In this case ;; you can use the special "%%{", "%%}", etc. comments (called indent comment shift operators) to ;; adjust indentation as in: ;; ;; %if mode == "standard" ;; if (a > 1) { ;; if (b > 0) { ;; % = % + b; ;; %else ;; if (x > 0) { ;; if (y > 0) { ;; % = % + y; ;; %endif ;; %%{{{ ;; this adjusts indentation by 12 ;; % = % ;; } ;; } ;; %%} ;; this adjusts indentation by -4 ;; ;; Another use of the indent comment shift operators is with %openfile to write ;; target language output comments, e.g. ;; ;; %function GetHeader() void ;; %openfile header ;; %%} ;; Unindented C ;; comment text ;; %%{ ;; %closefile header ;; %return header ;; %endfunction ;; ;; Using %openfile/%closefile is helpful when creating messages and this often requires that the ;; content starts at a specific column, e.g. column 1 (zero based), such that when the lines of the ;; message are combined into a single line the words at the line boundaries are joined by spaces, ;; e.g. ;; ;; %function GetMessage() void ;; %openfile message ;; %%{1} ;; This is a message ;; that spans multiple lines ;; %%{4} ;; %closefile ;; %endfunction ;; ;; Issues: ;; 1. Font's for continuations multiline statements are not correct, i.e. ;; %assign a = 1 + ... ;; 2 + ... ;; 3 ;; will have the "2 + ..." and "3" line shown using tlc-font-lock-output-code. ;; To fix, we probably need to use Multiline Font Lock constructs. ;;; History: ;; ;; 10Sep1998 by Eric M. Ludlam ;; Posted First revision onto the FTP site. ;; ;; 06Oct2005 Peter S galbraith ;; Minor changes for: ;; - support customization. ;; - added autoload cookies. ;; - CVS storage elsewhere without changing the version number. ;; ;; Recent history is in the ChangeLog and the matlab-emacs repository. ;;; Code: (defun tlc-version () "Display the current version of TLC mode." (interactive) (message tlc-version)) (defgroup tlc nil "Major mode for editing tlc files." :group 'languages) (defcustom tlc-mode-hook nil "*List of functions to call on entry to TLC mode." :group 'tlc :type 'hook) (defvar tlc-syntax-table nil "Syntax table used in an TLC file.") (unless tlc-syntax-table (setq tlc-syntax-table (make-syntax-table (standard-syntax-table))) ;; Multiline comments: /% ... %/ ;; Single line comments: %% ... (modify-syntax-entry ?/ ". 14c" tlc-syntax-table) (modify-syntax-entry ?% ". 123" tlc-syntax-table) (modify-syntax-entry ?\n ">" tlc-syntax-table) ;; Strings (modify-syntax-entry ?\" "\"" tlc-syntax-table) ;; % support: (modify-syntax-entry ?< "(>" tlc-syntax-table) (modify-syntax-entry ?> ")>" tlc-syntax-table)) (defvar tlc-mode-map (let ((km (make-sparse-keymap))) (define-key km "\C-m" 'tlc-return) (define-key km [return] 'tlc-return) (define-key km "\C-i" 'tlc-indent) km) "Keymap for `tlc-mode'.") (defvar tlc-font-lock-output-code 'tlc-font-lock-output-code "Face for output code.") (defface tlc-font-lock-output-code '((((class grayscale) (background light)) (:foreground "DimGray" :underline t)) (((class grayscale) (background dark)) (:foreground "LightGray" :underline t)) (((class color) (background light)) (:foreground "DarkGreen")) (((class color) (background dark)) (:foreground "chartreuse")) (t (:underline t))) "Font Lock mode face used to highlight tlc keywords." :group 'tlc) (defcustom tlc-keywords '("CAST" "EXISTS" "FEVAL" "FILE_EXISTS" "FORMAT" "FIELDNAMES" "GETFIELD" "GENERATE" "GENERATE_FILENAME" "GENERATE_FORMATTED_VALUE" "GENERATE_FUNCTION_EXISTS" "GENERATE_TYPE" "GENERATE_TYPE_FUNCTION_EXISTS" "GET_COMMAND_SWITCH" "IDNUM" "IMAG" "INT8MAX" "INT8MIN" "INT16MAX" "INT16MIN" "INT32MAX" "INT32MIN" "ISEQUAL" "ISFIELD" "ISINF" "ISNAN" "ISFINITE" "NULL_FILE" "NUMTLCFILES" "OUTPUT_LINES" "SIZE" "STDOUT" "STRING" "STRINGOF" "SYSNAME" "TLCFILES" "TLC_TIME" "TLC_FALSE" "TLC_TRUE" "TLC_VERSION" "TYPE" "UINT8MAX" "UINT16MAX" "UINT32MAX" "UINTWHITE_SPACE" "WILL_ROLL") "Built-in function keywords to highlight in TLC." :type '(repeat (string :tag "keyword")) :group 'tlc) (defvar tlc-font-lock-keywords (list ;; %function keyword '("^%function\\s-+\\(\\sw+\\)\\s-*(" 1 font-lock-function-name-face) '("^%function\\s-+\\(\\sw+\\)\\s-*(" ("\\s-*\\(\\sw+\\)\\s-*[,)]" nil nil (1 font-lock-variable-name-face))) ;; Single line comments: %% text ;; Special "%%{", "%%}", etc. comments '("\\(?:%%\\)\\({+\\|{+[0-9]+}\\)\\s-*$" 1 'bold prepend) '("\\(?:%%\\)\\(}+\\)\\s-*$" 1 'bold prepend) ;; Target language output code '("\\(^[ \t]*\\([^ \n\t%]\\|%<\\)[^\n]*\\)$" 1 tlc-font-lock-output-code append) ;; Keywords, e.g., %if '("\\(^\\|\\s-\\)\\(%[^% \t(\n>]+\\)\\>" 2 font-lock-keyword-face) ;; %assign keyword '("%assign\\s-+:*\\([_a-zA-Z0-9.]+\\)\\s-*\\($\\|=\\)" 1 font-lock-variable-name-face) ;; %exit, %warning, %error, %trace keywords '("%\\(exit\\|warning\\|error\\|trace\\) \\([^\n]+\\)$" 2 font-lock-string-face prepend) ;; % expansions '("\\(%<[^%\n>]+>\\)" 1 font-lock-constant-face prepend) ;; Built-in functions, e.g. EXISTS (list (concat "\\<\\(" (regexp-opt tlc-keywords) "\\)\\>") 1 'font-lock-type-face) '("[^.]\\(\\.\\.\\.\\)$" 1 'underline prepend) ) "List of keywords for nicely coloring X defaults.") ;;;###autoload (define-derived-mode tlc-mode prog-mode "TLC" () "Major mode for editing Tlc files, or files found in tlc directories." (kill-all-local-variables) (setq major-mode 'tlc-mode) (setq mode-name "TLC") (use-local-map tlc-mode-map) (set-syntax-table tlc-syntax-table) (make-local-variable 'comment-start) (make-local-variable 'comment-end) (make-local-variable 'comment-start-skip) (setq comment-start "%% " comment-end "") (setq comment-start-skip "%%\\|/%") (make-local-variable 'indent-line-function) (setq indent-line-function 'tlc-indent) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((tlc-font-lock-keywords) nil ; do not do string/comment highlighting nil ; keywords are case sensitive. ;; This puts _ as a word constituent, ;; simplifying our keywords significantly ((?_ . "w")))) (tlc-version) (save-excursion (goto-char (point-min)) (run-hooks 'tlc-mode-hook))) (defun tlc-return () "Handle carriage return in `tlc-mode'." (interactive) (delete-horizontal-space) (newline) (tlc-indent)) (defun tlc-indent () "Indent the current line to the indentation of the previous line." (interactive) (let (curr-indent new-indent) (save-excursion (save-restriction (setq curr-indent (current-indentation)) (setq new-indent (tlc--calc-indentation)))) (if (= curr-indent new-indent) (when (< (current-column) curr-indent) (goto-char (+ (line-beginning-position) curr-indent))) ;; else indent (save-restriction (let ((curr-column (current-column))) (beginning-of-line) (delete-horizontal-space) (indent-to new-indent) ;; now current-column is new-indent (when (> curr-column curr-indent) ;; Suppose prior to tlc-indent, we had the following with cursor at column 17: ;; 0123456789012345678901234567890 ;; %assign var = MyFcn(p1) ;; ^ ;; after indentation, we move the %assign to column 8 (because it is within another ;; context which requires we have indentation of 8): ;; 0123456789012345678901234567890 ;; %assign var = MyFcn(p1) ;; ^ ;; we want the cursor to remain at 'M' and that's what the goto-char does. ;; ;; If the cursor was anywhere before the %assign, we want the cursor to be at the ;; '%' and that's what the indent-to function does, which is why we guard the goto-char ;; with (> curr-column curr-indent). (goto-char (+ (line-beginning-position) new-indent (- curr-column curr-indent))))))))) (defvar tlc--indent-because-of-continuation nil) (defun tlc--calc-indentation () "Calculate the indentation of this line." (beginning-of-line) (if (and (looking-at "\\s-*%%{[0-9]+}\\s-*$") (not (tlc--in-multiline-comment))) ;; %%{N} means the place %%{N} at column 0 and the following line goes to column N 0 ;; Else calculate indentation based on current line PLUS the context of the prior line (let ((i-col (cond ((and (looking-at "\\s-*\\(?:\ \\(?:\\(?:%end\\(switch\\|roll\\|with\\|for\\|foreach\\|while\\|function\\)\\)\\>\\)\ \\|}\\)") (not (tlc--in-multiline-comment))) -4) ((and (looking-at "\\s-*\\(%case\\|%default\\)\\>") (not (tlc--in-multiline-comment))) -2) ;; %%} means shift by -4, %%}} means shift by -8, etc. ((and (looking-at "\\s-*%%\\(}+\\)\\s-*$") (not (tlc--in-multiline-comment))) (* -4 (- (match-end 1) (match-beginning 1)))) ;; (t 0))) (is-tlc-if-part (and (looking-at "\\s-*%\\(?:else\\|elseif\\|endif\\)") ;; part of a %if? (not (tlc--in-multiline-comment)))) (percent-in-multiline-comment (and (looking-at "\\s-*%") (tlc--in-multiline-comment)))) ;; Walk up to calculate the indent based on the construct we are within and then add it to ;; i-col for the current construct. (setq tlc--indent-because-of-continuation nil) (if (bobp) (current-indentation) (save-excursion (tlc--indent-move-up is-tlc-if-part) (cond ((bobp) (setq i-col (+ i-col (tlc--calc-next-indentation)))) ;; '%' line following a "/%" line, if so add 1 ((and percent-in-multiline-comment (looking-at "\\s-*/%")) (setq i-col (1+ (current-indentation)))) ;; Align %elsif, %else, %endif with corresponding %if? (is-tlc-if-part (setq i-col (current-indentation))) (t (setq i-col (+ (current-indentation) (if (and tlc--indent-because-of-continuation (or (> 0 i-col) is-tlc-if-part)) i-col (+ i-col (tlc--calc-next-indentation))))) (if (< i-col 0) (setq i-col 0)))) i-col))))) (defun tlc--indent-move-up (is-tlc-if-part) "Move to first prior non-blank line or matching %if, %else, %endif when IS-TLC-IF-PART is t. Specify IS-TLC-IF-PART as t, if current line is %else, %elsif, %endif to align the %if statements." (let ((n-if-statements-to-skip 0) ;; num %if statements to skip over when is-tlc-if-part is t done) (while (not done) ;; ;; Move up to first non-blank line ;; (forward-line -1) (beginning-of-line) (while (and (not (bobp)) (looking-at "^\\s-*$")) ;; skip blank lines (forward-line -1)) ;; ;; Align %elseif, %else, %endif with the prior TLC statement ;; (if (and (not (bobp)) is-tlc-if-part) ;; If within a non-TLC statement, look up until we find the matching if "part" (if (looking-at "\\s-*%endif\\>") (setq n-if-statements-to-skip (1+ n-if-statements-to-skip)) (if (> n-if-statements-to-skip 0) (if (looking-at "\\s-*%if\\>") (setq n-if-statements-to-skip (1- n-if-statements-to-skip))) (setq done (looking-at "\\s-*%\\(?:if\\|elseif\\|else\\)\\>")))) (setq done t))))) (defun tlc--calc-next-indentation () "Calculate indentation for the next line based on the current line." (if (and (looking-at "\\s-*%%") (not (tlc--in-multiline-comment))) (cond ((looking-at "\\s-*%%\\({+\\)\\s-*$") ;; %%{ means shift by 4, %%{{ means shift by 8, etc. (* 4 (- (match-end 1) (match-beginning 1)))) ((looking-at "\\s-*%%{\\([0-9]+\\)}\\s-*$") ;; %%{N} means the place %%{N} at column 0 and the following line goes to column N (string-to-number (buffer-substring (match-beginning 1) (match-end 1)))) (t 0)) ;; Else compute indent based on language element (+ ;; Include offset based on language element, e.g. %function means to indent by 4 (cond ((save-excursion (and (not (tlc--assignment-continuation-p)) (tlc--beginning-of-statement)) (or (and (looking-at "\\s-*%\\(?:switch\\|roll\\|with\\|if\\|for\\|\ foreach\\|while\\|else\\|elseif\\|function\\)\\>") ;; Do not indent "tlc guards", i.e. ;; %if EXISTS(::_FILE_NAME_) == 0 ;; %assign _FILE_NAME_ = 1 ;; ;; %endif (not (looking-at "\\s-*%if\\s-+\\(?:\ EXISTS(\"?\\(?:::\\)?_[A-Z_0-9]+_\"?)\\s-*==\\s-*\\(?:0\\|TLC_FALSE\\)\\|\ !EXISTS(\"?\\(?:::\\)?_[A-Z_0-9]+_\"?)\\)")) (not (tlc--in-multiline-comment))) (and (looking-at "\\s-*/%") (tlc--in-multiline-comment)))) 4) ((and (save-excursion (and (not (tlc--assignment-continuation-p)) (tlc--beginning-of-statement)) (looking-at "\\s-*%\\(?:case\\|default\\)\\>")) (not (tlc--in-multiline-comment))) 2) ;; End of multiline comment? ((and (looking-at "\\s-*%/") (tlc--in-multiline-comment)) (if (= (% (current-indentation) 2) 1) ;; When current-indentation is odd, we've shifted the "%/" by 1, so need to shift it ;; back -1 0)) ;; Continuation? ((and (tlc--assignment-continuation-p) (save-excursion (forward-line -1) (not (tlc--assignment-continuation-p))) (not (tlc--in-multiline-comment))) (setq tlc--indent-because-of-continuation t) 4) ;; Open bracket target language line, e.g. "void foo() {", "struct {\", etc. ((and (save-excursion (end-of-line) (re-search-backward "[^[:space:]]" (line-beginning-position) t) (when (and (> (current-column) 0) ;; Do we have line ending in \, if so remove it (looking-at "\\\\")) (backward-char) (when (looking-at "\\s-") (re-search-backward "[^[:space:]]" (line-beginning-position) t))) (looking-at "{")) (not (tlc--in-multiline-comment))) 4) (t 0)) ;; Don't include continuation if not in a continuation block (if (and (not (tlc--line-special)) (not (tlc--assignment-continuation-p)) (save-excursion (forward-line -1) (tlc--assignment-continuation-p)) (not (tlc--in-multiline-comment))) -4 0)))) (defun tlc--beginning-of-statement () "Goto the beginning of a statement, skipping over continuation lines." (beginning-of-line) (if (not (save-excursion (forward-line -1) (tlc--assignment-continuation-p))) nil (forward-line -1) (while (tlc--assignment-continuation-p) (forward-line -1)) (forward-line 1) (beginning-of-line))) (defun tlc--line-special () "Return t if the current line is a special language line." (save-excursion (save-match-data (beginning-of-line) (looking-at "\\s-*\\(?:%[^<]\\|}\\)")))) (defun tlc--assignment-continuation-p () "See if continuation lines should be indented." (save-excursion (end-of-line) (when (> (current-column) 2) (forward-char -3) (looking-at "\\.\\.\\.")))) (defun tlc--in-multiline-comment () "Return t we are in a multiline comment." ;; One way to do this is to use ;; (save-excursion (and (re-search-backward "/%\\|%/" nil t) (looking-at "/%"))) ;; however this fails on something like the following where /% is in a string ;; %assign foo = "dir/%.h" ;; %if a ;; ^------- point ;; From ;; https://emacs.stackexchange.com/questions/14269/how-to-detect-if-the-point-is-within-a-comment-area ;; we can use syntax-ppss (nth 4 (syntax-ppss))) ;;; Add to mode list ;;;###autoload (add-to-list 'auto-mode-alist '("\\.tlc\\'" . tlc-mode)) (provide 'tlc) ;;; tlc.el ends here ;; LocalWords: Ludlam eludlam galbraith psg debian defun defcustom setq keymap defface grayscale ;; LocalWords: IDNUM NUMTLCFILES STRINGOF SYSNAME TLCFILES UINTWHITE repeat:nil prog calc endswitch ;; LocalWords: bobp nexti progn alist el stackexchange ppss openfile closefile matlab-mode/matlab-shell-gud.el0000664000175000017500000012117014611713120017420 0ustar sebastiensebastien;;; matlab-shell-gud.el --- GUD support in matlab-shell. ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; GUD (grand unified debugger) support for MATLAB shell. ;; ;; Includes setting up mlgud mode in the shell, and all filters, etc specific ;; to supporting mlgud. (require 'matlab-shell) (eval-and-compile (require 'mlgud) (require 'eieio) ) ;;; Code: (defcustom matlab-shell-debug-tooltips-p nil "*Enable tooltips displaying data values when at the K>> prompt. Disable this option if the tooltips are too slow in your setup." :group 'matlab-shell :type 'boolean) (defvar gud-matlab-debug-active nil "Non-nil if MATLAB has a K>> prompt up.") (defvar gud-matlab-debug-activate-hook nil "Hooks run when MATLAB detects a K>> prompt after a >> prompt.") (defvar gud-matlab-debug-deactivate-hook nil "Hooks run when MATLAB detects a >> prompt after a K>> prompt.") (defvar gud-matlab-tool-bar-map nil "Toolbar keymap used when in MATLAB debug mode.") (declare-function matlab-netshell-eval "matlab-netshell" (mode)) (defmacro matlab-at-fcn (cmd) "Define CMD to be a GUD command that works w/ shell or netshell." ;; Note `arg' comes from mlgud-def declaration `(if (matlab-shell-active-p) (mlgud-call (concat ,cmd "%%") arg) (if (matlab-netshell-active-p) (matlab-netshell-eval (mlgud-format-command ,cmd arg)) (error "No MATLAB shell active")))) (defmacro matlab-gud-fcn (cmd) "Define CMD forms to be sent to a MATLAB shell." ;; Note `arg' comes from mlgud-def declaration `(if gud-matlab-debug-active (matlab-at-fcn ,cmd) (error "MATLAB debugging not active"))) ;;;###autoload (defun matlab-shell-mode-gud-enable-bindings () "Enable GUD features for `matlab-shell' in the current buffer." ;; Make sure this is safe to use mlgud to debug MATLAB (when (not (fboundp 'mlgud-def)) (error "Your Emacs is missing `mlgud-def' which means matlab-shell won't work correctly. Stopping")) (mlgud-def mlgud-break (matlab-at-fcn "ebstop in %d%f at %l") "Set breakpoint at current line.") (mlgud-def mlgud-remove (matlab-at-fcn "ebclear in %d%f at %l") "Remove breakpoint at current line.") (mlgud-def mlgud-step (matlab-gud-fcn "dbstep in") "Step one source line, possibly into a function.") (mlgud-def mlgud-next (matlab-gud-fcn "dbstep %p") "Step over one source line.") (mlgud-def mlgud-cont (matlab-gud-fcn "dbcont") "Continue execution.") (mlgud-def mlgud-stop-subjob (matlab-gud-fcn "dbquit") "Quit debugging.") ;; mlgud toolbar stop (mlgud-def mlgud-finish (matlab-gud-fcn "dbstep out") "Finish executing current function.") (mlgud-def mlgud-up (matlab-gud-fcn "dbup") "Up N stack frames (numeric arg).") (mlgud-def mlgud-down (matlab-gud-fcn "dbdown") "Down N stack frames (numeric arg).") (mlgud-def mlgud-list-breakpoints (matlab-at-fcn "ebstatus") "List breakpoints") (mlgud-def mlgud-show-stack (matlab-at-fcn "ebstack") "Show stack") ;; using (mlgud-def mlgud-print "%e" "\C-p" "Eval expression at point") fails ;; (mlgud-def mlgud-print "% mlgud-print not available" "\C-p" "mlgud-print not available.") (when window-system (setq gud-matlab-tool-bar-map (let ((map (make-sparse-keymap))) (dolist (x '((mlgud-break . "gud/break") (mlgud-remove . "gud/remove") (mlgud-cont . "gud/cont") (mlgud-next . "gud/next") (mlgud-step . "gud/step") (mlgud-finish . "gud/finish") (mlgud-stop-subjob . "gud/stop") (mlg-show-stack . "gud/all") (mlgud-list-breakpoints . "describe") )) (tool-bar-local-item-from-menu (car x) (cdr x) map matlab-mode-map)) map)) ) (if (fboundp 'mlgud-make-debug-menu) (mlgud-make-debug-menu)) (when (boundp 'tool-bar-map) ; not --without-x (kill-local-variable 'tool-bar-map)) ) ;;;###autoload (defun matlab-shell-gud-startup () "Configure GUD when a new `matlab-shell' is initialized." (mlgud-mode) ;; type of mlgud mode (setq mlgud-minor-mode 'matlab) ;; This starts us supporting mlgud tooltips. (add-to-list 'mlgud-tooltip-modes 'matlab-mode) (make-local-variable 'mlgud-marker-filter) (setq mlgud-marker-filter 'gud-matlab-marker-filter) (make-local-variable 'mlgud-find-file) (setq mlgud-find-file 'gud-matlab-find-file) (global-matlab-shell-inactive-gud-minor-mode 1) ;; Setup our debug tracker. (add-hook 'matlab-shell-prompt-appears-hook #'gud-matlab-debug-tracker) (mlgud-set-buffer)) ;;; GUD Functions (defun gud-matlab-massage-args (file args) "Argument message for starting matlab file. I don't think I have to do anything, but I'm not sure. FILE is ignored, and ARGS is returned." args) (defun gud-matlab-find-file (f) "Find file F when debugging frames in MATLAB." (save-excursion (let* ((realfname (if (string-match "\\.\\(p\\)$" f) (progn (aset f (match-beginning 1) ?m) f) f)) (buf (find-file-noselect realfname t))) (set-buffer buf) (if (fboundp 'mlgud-make-debug-menu) (mlgud-make-debug-menu)) buf))) ;;; GUD Filter Function ;; ;; MATLAB's process filter handles output from the MATLAB process and ;; interprets it for formatting text, and for running the debugger. (defvar matlab-shell-gud--marker-acc "") (make-variable-buffer-local 'matlab-shell-gud--marker-acc) (defvar gud-matlab-marker-regexp-plain-prompt "^K?>>" "Regular expression for finding a prompt.") (defvar gud-matlab-marker-regexp-K>> "^K>>" "Regular expression for finding a file line-number.") (defvar gud-matlab-marker-regexp->> "^>>" "Regular expression for finding a file line-number.") (defvar gud-matlab-dbhotlink nil "Track if we've sent a dbhotlink request.") (make-variable-buffer-local 'gud-matlab-dbhotlink) (defun gud-matlab-marker-filter (string) "Filters STRING for the Unified Debugger based on MATLAB output." (setq matlab-shell-gud--marker-acc (concat matlab-shell-gud--marker-acc string)) (let ((output "") (frame nil)) ;; ERROR DELIMITERS ;; Newer MATLAB's wrap error text in {^H }^H characters. ;; Convert into something COMINT won't delete so we can scan them. (while (string-match "{" matlab-shell-gud--marker-acc) (setq matlab-shell-gud--marker-acc (replace-match matlab-shell-errortext-start-text t t matlab-shell-gud--marker-acc 0))) (while (string-match "}" matlab-shell-gud--marker-acc) (setq matlab-shell-gud--marker-acc (replace-match matlab-shell-errortext-end-text t t matlab-shell-gud--marker-acc 0))) ;; DEBUG PROMPTS (when (string-match gud-matlab-marker-regexp-K>> matlab-shell-gud--marker-acc) ;; Newer MATLAB's don't print useful info. We'll have to ;; search backward for the previous line to see if a frame was ;; displayed. (when (and (not frame) (not gud-matlab-dbhotlink)) (let ((dbhlcmd (if matlab-shell-echoes "dbhotlink()%%%\n" ;; If no echo, force an echo "disp(['dbhotlink()%%%' newline]);dbhotlink();\n"))) ;;(when matlab-shell-io-testing (message "!!> [%s]" dbhlcmd)) (process-send-string (get-buffer-process mlgud-comint-buffer) dbhlcmd) ) (setq gud-matlab-dbhotlink t) ) ) ;; If we're forced to ask for a stack hotlink, we will see it come in via the ;; process output. Don't output anything until a K prompt is seen after the display ;; of the dbhotlink command. (when gud-matlab-dbhotlink (let ((start (string-match "dbhotlink()%%%" matlab-shell-gud--marker-acc)) (endprompt nil)) (if start (progn (setq output (substring matlab-shell-gud--marker-acc 0 start) matlab-shell-gud--marker-acc (substring matlab-shell-gud--marker-acc start)) ;; The hotlink text will persist until we see the K prompt. (when (string-match gud-matlab-marker-regexp-plain-prompt matlab-shell-gud--marker-acc) (setq endprompt (match-end 0)) ;; (when matlab-shell-io-testing (message "!!xx [%s]" (substring matlab-shell-gud--marker-acc 0 endprompt))) ;; We're done with the text! ;; Capture the text that describes the new stack frame. (save-match-data (let* ((expr-end (match-beginning 0)) (m1 (string-match "dbhotlink()%%%\n" matlab-shell-gud--marker-acc)) (expr-start (match-end 0)) (expression (substring matlab-shell-gud--marker-acc expr-start expr-end))) (when (> (length expression) 0) (condition-case ERR (let ((forms (read expression))) (when forms ;;(message "About to evaluate forms: \"%S\"" forms) (eval forms))) (error (message "Failed to evaluate dbhotlink expression: \"%s\"" expression) (message "Error is: %S" ERR) ) )) )) ;;Remove it from the accumulator. (setq matlab-shell-gud--marker-acc (substring matlab-shell-gud--marker-acc endprompt)) ;; If we got all this at the same time, push output back onto the accumulator for ;; the next code bit to push it out. (setq matlab-shell-gud--marker-acc (concat output matlab-shell-gud--marker-acc) output "" gud-matlab-dbhotlink nil) )) ;; Else, waiting for a link, but hasn't shown up yet. ;; TODO - what can I do here to fix var setting if it gets ;; locked? (when (string-match gud-matlab-marker-regexp->> matlab-shell-gud--marker-acc) ;; A non-k prompt showed up. We're not going to get out request. (setq gud-matlab-dbhotlink nil)) ))) ;; This if makes sure that the entirety of an error output is brought in ;; so that matlab-shell-mode doesn't try to display a file that only partially ;; exists in the buffer. Thus, if MATLAB output: ;; error: /home/me/my/mo/mello.m,10,12 ;; All of that is in the buffer, and it goes to mello.m, not just ;; the first half of that file name. ;; The below used to match against the prompt, not \n, but then text that ;; had error: in it for some other reason wouldn't display at all. (if (and matlab-prompt-seen ;; don't pause output if prompt not seen gud-matlab-dbhotlink ;; pause output if waiting on debugger ) ;; We could be collecting debug info. Wait before output. nil ;; Finish off this part of the output. None of our special stuff ;; ends with a \n, so display those as they show up... (while (string-match "^[^\n]*\n" matlab-shell-gud--marker-acc) (setq output (concat output (substring matlab-shell-gud--marker-acc 0 (match-end 0))) matlab-shell-gud--marker-acc (substring matlab-shell-gud--marker-acc (match-end 0)))) (if (string-match (concat gud-matlab-marker-regexp-plain-prompt "\\s-*$") matlab-shell-gud--marker-acc) (setq output (concat output matlab-shell-gud--marker-acc) matlab-shell-gud--marker-acc "")) ;; Check our output for a prompt, and existence of a frame. ;; If this is true, throw out the debug arrow stuff. (if (and (string-match (concat gud-matlab-marker-regexp->> "\\s-*$") output) mlgud-last-last-frame) (progn ;; Clean up mlgud stuff. (setq overlay-arrow-position nil mlgud-last-last-frame nil mlgud-overlay-arrow-position nil) ;; If stack is showing, clean it up. (let* ((buff (mlg-set-stack nil)) (win (get-buffer-window buff))) (when win (select-window win) (mlg-stack-quit) )) ;; Refresh stuff (sit-for 0) )) ;; Check for any text that would be embarrassing to display partially. ;; If we don't see any, feel free to dump the rest of the accumulation buffer (unless (or (string-match (regexp-quote "[%s] [%s]" output matlab-shell-gud--marker-acc)) ;;(message "Looking for prompt in %S" output) (when (and (not matlab-shell-suppress-prompt-hooks) (string-match gud-matlab-marker-regexp-plain-prompt output)) ;; Now that we are about to dump this, run our prompt hook. ;;(message "PROMPT!") (setq matlab-shell-prompt-hook-cookie t)) output)) ;;; Stack tracking ;; (defclass mlg-stack-frame () ((file :initarg :file :type string :documentation "The filename this frame belongs to.") (name :initarg :name :type string :documentation "The name of the location of this frame") (line :initarg :line :type integer :documentation "The line number for this frame")) "A single stack frame from MATLAB.") (cl-defmethod mlg-print ((frame mlg-stack-frame) longestname) "Use print to output this stack FRAME. LONGESTNAME specifies the how long the longest name we can expect is." (let* ((namefmt (concat "%" (number-to-string (or longestname 10)) "s")) (str (concat (propertize (format namefmt (oref frame name)) 'face 'font-lock-function-name-face) " " (propertize (format "%3d" (oref frame line)) 'face 'bold) " " (propertize (oref frame file) 'face 'font-lock-constant-face)))) (setq str (propertize str 'object frame)) str)) (defvar mlg-stack nil "The last stack sent to us from MATLAB.") (defvar mlg-frame nil "The last frame sent to use from MATLAB.") (defun mlg-set-stack (newstack) "Specify a NEWSTACK provided by MATLAB to replace the old one." (setq mlg-stack nil) (dolist (L newstack) (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) ;;(message "Updated Stack") ) (defun mlg-set-stack-frame (newframe) "Specify a NEWFRAME provided by MATLAB we should visit." (setq mlg-frame newframe) (mlg-show-stack) (mlg-show-frame newframe) ) (defun mlg-set-stack-frame-via-gud (newframe) "Specify a NEWFRAME provided by MATLAB we should visit." (setq mlg-frame newframe) (let ((file (oref (nth (1- newframe) mlg-stack) file)) (line (oref (nth (1- newframe) mlg-stack) line))) (if (< line 0) (setq line (- line))) (setq mlgud-last-frame (cons file line)) ;;(message "Gud FRAME set to %S" mlgud-last-frame) ) ) (defun mlg-show-frame (&optional frame) "Setup windows to show FRAME from the current stack frame." (let ((newframe (or frame mlg-frame))) (if (and mlg-stack (<= newframe (length mlg-stack))) ;; Make sure we have a stack window. (let* ((buff (get-buffer "*MATLAB stack*")) (win (get-buffer-window buff))) (if (or (not buff) (not win)) (mlg-show-stack) ;; else, do refresh stuff. (select-window win)) ;; Still around, go do it. (goto-char (point-min)) (forward-line (1- frame)) (mlg-stack-choose) ) ;; Else no frame. Look for the window, and close it. (let* ((buff (get-buffer "*MATLAB stack*")) (win (get-buffer-window buff))) (when win (delete-window win))) ))) (defun mlg-refresh-stack-buffer () "Refresh the buffer displaying stack." (save-excursion (let ((buff (get-buffer-create "*MATLAB stack*")) (namelen 5) (inhibit-read-only t)) (dolist (S mlg-stack) (when (> (length (oref S name)) namelen) (setq namelen (length (oref S name))))) (set-buffer buff) (erase-buffer) (let ((cnt 1)) (dolist (F mlg-stack) (insert (format "%2d" cnt)) (if (and mlg-frame (= cnt mlg-frame)) (insert " >> ") (insert " -- ")) (insert (mlg-print F namelen) "\n") (setq cnt (1+ cnt)))) (mlg-stack-mode) (goto-char (point-min)) (current-buffer)))) (defun mlg-show-stack () "Display the MATLAB stack in an interactive buffer." (interactive) (let ((buff (mlg-refresh-stack-buffer))) (display-buffer buff '((display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer)) ) (select-window (get-buffer-window buff)) (goto-char 3) )) (defvar mlg-stack-mode-map (let ((km (make-sparse-keymap))) (define-key km [return] 'mlg-stack-choose) (define-key km "q" 'mlg-stack-quit) (define-key km "n" 'mlg-stack-next) (define-key km "p" 'mlg-stack-prev) (define-key km [mouse-2] 'mlg-stack-click) (define-key km [mouse-1] 'mlg-stack-click) km) "Keymap used in MATLAB stack mode.") ;; Need this to fix weird problem in define-derived-mode (defvar mlg-stack-mode-syntax-table (make-syntax-table) "Syntax table used in `matlab-shell-help-mode'.") (define-derived-mode mlg-stack-mode fundamental-mode "MStack" "Major mode for viewing a MATLAB stack. Commands: \\{mlg-stack-mode-map}" :syntax-table mlg-stack-mode-syntax-table (setq buffer-read-only t) ) (defun mlg-stack-quit () "Quit the MATLAB stack view." (interactive) (if (= (length (window-list)) 1) (bury-buffer) (delete-window (selected-window)))) (defun mlg-stack-next () "Visit stack on next line." (interactive) (forward-line 1) (forward-char 2) (mlg-stack-choose)) (defun mlg-stack-prev () "Visit stack on next line." (interactive) (forward-line -1) (forward-char 2) (mlg-stack-choose)) (defun mlg-stack-click (e) "Click on a stack frame to visit it. Must be bound to event E." (interactive "e") (mouse-set-point e) (mlg-stack-choose)) (defun mlg-stack-choose () "Choose the stack the under the cursor. Visit the file presented in that stack frame." (interactive) (let ((topic nil) (fun nil) (p (point))) (save-excursion (beginning-of-line) (forward-char 10) (let* ((sf (get-text-property (point) 'object)) (f (oref sf file)) (l (oref sf line)) (buff (find-file-noselect f t))) (display-buffer buff '((display-buffer-reuse-window display-buffer-use-some-window) (inhibit-same-window . t)) ) (let ((win (selected-window))) (select-window (get-buffer-window buff)) (goto-char (point-min)) (forward-line (1- l)) (select-window win)) )))) ;;; Breakpoint Trackers ;; (defclass mlg-breakpoint () ((file :initarg :file :type string :documentation "The filename this breakpoint belongs to.") (name :initarg :name :type string :documentation "Name of the function this breakpoint is in.") (line :initarg :line :type integer :documentation "The line number for this breakpoint") (overlay :documentation :default nil "The overlay indicating the presence of this breakpoint.") ) "Representation of a breakpoint. Used to track active breakpoints, and how to show them.") (cl-defmethod mlg-print ((break mlg-breakpoint) longestname) "Use print to output this breakpoint BREAK. LONGESTNAME specifies the how long the longest name we can expect is." (let* ((namefmt (concat "%" (number-to-string (or longestname 10)) "s")) (str (concat (propertize (format namefmt (oref break name)) 'face 'font-lock-function-name-face) " " (propertize (format "%3d" (oref break line)) 'face 'bold) " " (propertize (oref break file) 'face 'font-lock-constant-face)))) (setq str (propertize str 'object break)) str)) (defvar matlab-gud-visible-breakpoints nil "List of breakpoints MATLAB has sent to us.") ;;;###autoload (defun mlg-reset-breakpoints () "Remove all cached breakpoints." (dolist (BP matlab-gud-visible-breakpoints) (mlg-deactivate BP)) (setq matlab-gud-visible-breakpoints nil)) (defun mlg-add-breakpoint (file fcn line) "Add a visible breakpoint to FILE at LINE." (let ((found nil)) (dolist (BP matlab-gud-visible-breakpoints) (when (and (string= (oref BP file) file) (= (oref BP line) line)) (setq found t))) (when (not found) (setq matlab-gud-visible-breakpoints (cons (make-instance 'mlg-breakpoint :file file :name fcn :line line) matlab-gud-visible-breakpoints)) (mlg-activate (car matlab-gud-visible-breakpoints)) )) ;; The first time breakpoints are added, make sure we can activate breakpoints ;; when new files are opened in a buffer. (add-hook 'matlab-mode-hook 'mlg-breakpoint-activate-buffer-opened-hook) ) (defun mlg-del-breakpoint (file fcn line) "Add a visible breakpoint to FILE at LINE." (let ((BPS matlab-gud-visible-breakpoints) (NBPS nil)) (while BPS (if (and (string= (oref (car BPS) file) file) (= (oref (car BPS) line) line)) ;; Deactivate (mlg-deactivate (car BPS)) ;; Not being removed, add to list. (setq NBPS (cons (car BPS) NBPS))) (setq BPS (cdr BPS))) (setq matlab-gud-visible-breakpoints (nreverse NBPS)))) (defface mlg-breakpoint-face (list (list t (list :background nil :foreground nil :underline "red1"))) "*Face to use to highlight breakpoints." :group 'matlab-shell) (cl-defmethod mlg-activate ((bp mlg-breakpoint)) "Activate breakpoint BP if needed." ;; yes overlay, but inactive (when (and (slot-boundp bp 'overlay) (oref bp overlay) (not (overlay-buffer (oref bp overlay)))) (oset bp overlay nil)) (let ((buff (find-buffer-visiting (oref bp file)))) ;; No overlay, and we can make one. (when (and (or (not (slot-boundp bp 'overlay)) (not (oref bp overlay))) buff) (with-current-buffer buff (goto-char (point-min)) (forward-line (1- (oref bp line))) (let ((ol (matlab-make-overlay (save-excursion (back-to-indentation) (point)) (point-at-eol) buff nil nil))) ;; Store it (oset bp overlay ol) ;; Setup cool stuff (matlab-overlay-put ol 'face 'mlg-breakpoint-face) (matlab-overlay-put ol 'before-string (propertize "#" 'display '(left-fringe filled-square matlab-shell-error-face)) )))) )) (cl-defmethod mlg-deactivate ((bp mlg-breakpoint)) "Deactivate this breakpoint BP." (when (slot-boundp bp 'overlay) (with-slots (overlay) bp (when (and overlay (overlayp overlay)) (delete-overlay overlay) (setq overlay nil))))) (defun mlg-breakpoint-activate-buffer-opened-hook () "Activate any breakpoints in a buffer when that buffer is read in." (if (not (matlab-shell-active-p)) (mlg-reset-breakpoints) ;; Still going, activate. (dolist (BP matlab-gud-visible-breakpoints) (mlg-activate BP) ))) (defun mlg-breakpoint-flush-and-reactivate () "Flush existing breakpoint markers, and reactivate." (interactive) (dolist (BP matlab-gud-visible-breakpoints) (mlg-deactivate BP) (mlg-activate BP)) ) (defun mlg-refresh-breakpoint-buffer () "Refresh the buffer displaying breakpoints." (save-excursion (let ((buff (get-buffer-create "*MATLAB breakpoints*")) (namelen 5) (inhibit-read-only t)) (dolist (S matlab-gud-visible-breakpoints) (when (> (length (oref S name)) namelen) (setq namelen (length (oref S name))))) (set-buffer buff) (erase-buffer) (let ((cnt 1)) (dolist (F matlab-gud-visible-breakpoints) (insert (format "%2d - " cnt)) (insert (mlg-print F namelen) "\n") (setq cnt (1+ cnt)))) (mlg-breakpoint-mode) (goto-char (point-min)) (current-buffer)))) (defun mlg-show-breakpoints () "Display the MATLAB stack in an interactive buffer." (interactive) (let ((buff (mlg-refresh-breakpoint-buffer))) (display-buffer buff '((display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer)) ) (select-window (get-buffer-window buff)) (goto-char 3) )) (defvar mlg-breakpoint-mode-map (let ((km (make-sparse-keymap))) (define-key km [return] 'mlg-breakpoint-choose) (define-key km "q" 'mlg-breakpoint-quit) (define-key km "n" 'mlg-breakpoint-next) (define-key km "p" 'mlg-breakpoint-prev) (define-key km [mouse-2] 'mlg-breakpoint-click) (define-key km [mouse-1] 'mlg-breakpoint-click) km) "Keymap used in MATLAB breakpoint mode.") ;; Need this to fix weird problem in define-derived-mode (defvar mlg-breakpoint-mode-syntax-table (make-syntax-table) "Syntax table used in `matlab-shell-help-mode'.") (define-derived-mode mlg-breakpoint-mode fundamental-mode "MBreakpoints" "Major mode for viewing a MATLAB breakpoints. Commands: \\{mlg-breakpoint-mode-map}" :syntax-table mlg-breakpoint-mode-syntax-table (setq buffer-read-only t) ) (defun mlg-breakpoint-quit () "Quit the MATLAB breakpoint view." (interactive) (if (= (length (window-list)) 1) (bury-buffer) (delete-window (selected-window)))) (defun mlg-breakpoint-next () "Visit breakpoint on next line." (interactive) (forward-line 1) (forward-char 2) (mlg-breakpoint-choose)) (defun mlg-breakpoint-prev () "Visit breakpoint on next line." (interactive) (forward-line -1) (forward-char 2) (mlg-breakpoint-choose)) (defun mlg-breakpoint-click (e) "Click on a breakpoint frame to visit it. Must be bound to event E." (interactive "e") (mouse-set-point e) (mlg-breakpoint-choose)) (defun mlg-breakpoint-choose () "Choose the breakpoint the under the cursor. Visit the file presented in that breakpoint frame." (interactive) (let ((topic nil) (fun nil) (p (point))) (save-excursion (beginning-of-line) (forward-char 10) (let* ((sf (get-text-property (point) 'object)) (f (oref sf file)) (l (oref sf line)) (buff (find-file-noselect f t))) (display-buffer buff '((display-buffer-reuse-window display-buffer-use-some-window) (inhibit-same-window . t)) ) (let ((win (selected-window))) (select-window (get-buffer-window buff)) (goto-char (point-min)) (forward-line (1- l)) (select-window win)) )))) ;;; K prompt state and hooks. ;; (defun gud-matlab-debug-tracker () "Function called when new prompts appear. Call debug activate/deactivate features." (save-excursion (let ((inhibit-field-text-motion t)) (goto-char (point-max)) (beginning-of-line) (cond ((and gud-matlab-debug-active (looking-at gud-matlab-marker-regexp->>)) ;; Debugger was active and we are back at prompt (setq gud-matlab-debug-active nil) (when (boundp 'tool-bar-map) ; not --without-x (with-current-buffer (matlab-shell-active-p) (kill-local-variable 'tool-bar-map))) (global-matlab-shell-gud-minor-mode -1) (global-matlab-shell-inactive-gud-minor-mode 1) (run-hooks 'gud-matlab-debug-deactivate-hook)) ((and (not gud-matlab-debug-active) (looking-at gud-matlab-marker-regexp-K>>)) ;; Debugger was NOT active and we are now in debug prompt (setq gud-matlab-debug-active t) (when (boundp 'tool-bar-map) ; not --without-x (with-current-buffer (matlab-shell-active-p) (setq-local tool-bar-map gud-matlab-tool-bar-map))) (global-matlab-shell-gud-minor-mode 1) (global-matlab-shell-inactive-gud-minor-mode -1) (run-hooks 'gud-matlab-debug-activate-hook)) (t ;; All clear )))) ) ;;; MATLAB SHELL GUD Minor Mode ;; ;; When K prompt is active, this minor mode is applied to frame buffers so ;; that GUD commands are easy to get to. (defvar matlab-shell-gud-minor-mode-map (let ((km (make-sparse-keymap)) (key ?\ )) (while (<= key ?~) (define-key km (string key) 'matlab-shell-gud-mode-help-notice) (setq key (1+ key))) (define-key km "h" 'matlab-shell-gud-mode-help) ;; mlgud bindings. (define-key km "b" 'mlgud-break) (define-key km "x" 'mlgud-remove) (define-key km "c" 'mlgud-cont) (define-key km " " 'mlgud-step) (define-key km "s" 'mlgud-step) (define-key km "n" 'mlgud-next) (define-key km "f" 'mlgud-finish) (define-key km "q" 'mlgud-stop-subjob) (define-key km "<" 'mlgud-up) (define-key km ">" 'mlgud-down) (define-key km "w" 'mlg-show-stack) (define-key km "v" 'mlgud-list-breakpoints) (define-key km "e" 'matlab-shell-gud-show-symbol-value) (define-key km "\C-x\C-q" 'matlab-shell-gud-mode-edit) ; like toggle-read-only km) "Keymap used by matlab mode maintainers.") (defun matlab-shell-gud-mode-help-notice () "Default binding for most keys in `matlab-shell-gud-minor-mode'. Shows a help message in the mini buffer." (interactive) (error "MATLAB shell GUD minor-mode: Press 'h' for help, 'e' to go back to editing")) (defun matlab-shell-gud-mode-help () "Show the default binding for most keys in `matlab-shell-gud-minor-mode'." (interactive) (describe-minor-mode 'matlab-shell-gud-minor-mode)) (defun matlab-shell-gud-mode-edit () "Turn off `matlab-shell-gud-minor-mode' so you can edit again." (interactive) (global-matlab-shell-gud-minor-mode -1)) (defun matlab-shell-gud-show-symbol-value (sym) "Show the value of the symbol SYM under point from MATLAB shell." (interactive (list (if (use-region-p) ;; Don't ask user anything, just take it. (buffer-substring-no-properties (region-beginning) (region-end)) (let ((word (matlab-read-word-at-point))) (read-from-minibuffer "MATLAB variable: " (cons word 0)))))) (let ((txt (matlab-shell-collect-command-output (concat "disp(" sym ")")))) (if (not (string-match "ERRORTXT" txt)) (matlab-output-to-temp-buffer "*MATLAB Help*" txt) (message "Error evaluating MATLAB expression")))) ;;;###autoload (define-minor-mode matlab-shell-gud-minor-mode "Minor mode activated when `matlab-shell' K>> prompt is active. This minor mode makes MATLAB buffers read only so simple keystrokes activate debug commands. It also enables tooltips to appear when the mouse hovers over a symbol when debugging. \\ Debug commands are: \\[matlab-shell-gud-mode-edit] - Edit file (toggle read-only) Allows editing file without causing MATLAB to exit debug mode. \\[mlgud-break] - Add breakpoint (ebstop in FILE at point) \\[mlgud-remove] - Remove breakpoint (ebclear in FILE at point) \\[mlgud-list-breakpoints] - List breakpoints (ebstatus) \\[mlgud-step] - Step (dbstep in) \\[mlgud-next] - Next (dbstep) \\[mlgud-finish] - Finish function (dbstep out) \\[mlgud-cont] - Continue (dbcont) \\[matlab-shell-gud-show-symbol-value] - Evaluate expression \\[mlg-show-stack] - Where am I (ebstack) \\[mlgud-stop-subjob] - Quit (dbquit)" nil " MGUD" matlab-shell-gud-minor-mode-map ;; Make the buffer read only (if matlab-shell-gud-minor-mode (progn ;; Enable (when (buffer-file-name) (setq buffer-read-only t)) (when matlab-shell-debug-tooltips-p (mlgud-tooltip-mode 1) (add-hook 'tooltip-functions 'gud-matlab-tooltip-tips) ) ;; Replace mlgud's toolbar which keeps stomping ;; on our toolbar. (make-local-variable 'mlgud-tool-bar-map) (setq mlgud-tool-bar-map gud-matlab-tool-bar-map) ) ;; Disable (when (buffer-file-name) (setq buffer-read-only (not (file-writable-p (buffer-file-name))))) ;; Always disable tooltips, in case configured while in the mode. (mlgud-tooltip-mode -1) (remove-hook 'tooltip-functions 'gud-matlab-tooltip-tips) ;; Disable the debug toolboar (when (boundp 'tool-bar-map) ; not --without-x (kill-local-variable 'tool-bar-map)))) ;;;###autoload (define-global-minor-mode global-matlab-shell-gud-minor-mode matlab-shell-gud-minor-mode (lambda () "Should we turn on in this buffer? Only if in a MATLAB mode." (when (eq major-mode 'matlab-mode) (matlab-shell-gud-minor-mode 1)))) ;;; MATLAB SHELL Inactive GUD Minor Mode (defvar matlab-shell-inactive-gud-minor-mode-map (let ((km (make-sparse-keymap))) (define-key km "\C-c\C-d\C-h" 'matlab-shell-inactive-gud-mode-help) ;; mlgud bindings when debugger is inactive. When inactive, only bindings such as mlgud-break ;; make sense. However, we also keep these bindings when the debugger is active for consistency. (define-key km (kbd "C-c C-d b") 'mlgud-break) (define-key km (kbd "C-c C-d x") 'mlgud-remove) (define-key km (kbd "C-c C-d c") 'mlgud-cont) (define-key km (kbd "C-c C-d SPC") 'mlgud-step) (define-key km (kbd "C-c C-d s") 'mlgud-step) (define-key km (kbd "C-c C-d n") 'mlgud-next) (define-key km (kbd "C-c C-d f") 'mlgud-finish) (define-key km (kbd "C-c C-d q") 'mlgud-stop-subjob) (define-key km (kbd "C-c C-d <") 'mlgud-up) (define-key km (kbd "C-c C-d >") 'mlgud-down) (define-key km (kbd "C-c C-d w") 'mlg-show-stack) (define-key km (kbd "C-c C-d v") 'mlgud-list-breakpoints) (define-key km (kbd "C-c C-d e") 'matlab-shell-gud-show-symbol-value) km) "Keymap used by matlab mode maintainers.") ;;;###autoload (define-minor-mode matlab-shell-inactive-gud-minor-mode "Minor mode activated when `matlab-shell' K>> prompt is inactive. \\ Debug commands are: \\[mlgud-break] - Add breakpoint (ebstop in FILE at point) \\[mlgud-remove] - Remove breakpoint (ebclear in FILE at point) \\[mlgud-list-breakpoints] - List breakpoints (ebstatus) " nil " I-MGUD" matlab-shell-inactive-gud-minor-mode-map ;; Always disable tooltips, in case configured while in the mode. (mlgud-tooltip-mode -1) (remove-hook 'tooltip-functions 'gud-matlab-tooltip-tips) (when (boundp 'tool-bar-map) ; not --without-x (kill-local-variable 'tool-bar-map))) (defun matlab-shell-inactive-gud-mode-help () "Show the default binding for most keys in `matlab-shell-gud-minor-mode'." (interactive) (describe-minor-mode 'matlab-shell-gud-minor-mode)) ;;;###autoload (define-global-minor-mode global-matlab-shell-inactive-gud-minor-mode matlab-shell-inactive-gud-minor-mode (lambda () "Should we turn on in this buffer? Only if in a MATLAB mode." (when (eq major-mode 'matlab-mode) (matlab-shell-inactive-gud-minor-mode 1)))) ;;; Tooltips ;; ;; Using the mlgud tooltip feature for a bunch of setup, but then ;; just override the tooltip fcn (see the mode) with this function ;; as an additional piece. (defun gud-matlab-tooltip-tips (event) "Implementation of the tooltip feature for MATLAB. Much of this was copied from `mlgud-tooltip-tips'. This function must return nil if it doesn't handle EVENT." (when (and (eventp event) (tooltip-event-buffer event)) (with-current-buffer (tooltip-event-buffer event) (when (and mlgud-tooltip-mode matlab-shell-gud-minor-mode (buffer-name mlgud-comint-buffer) ; might be killed ) (let ((expr (matlab-shell-gud-find-tooltip-expression event)) (txt nil)) (when expr (setq txt (matlab-shell-collect-command-output (concat "emacstipstring(" expr ")"))) (when (not (string-match "ERRORTXT" txt)) (tooltip-show (concat expr "=\n" txt) (or mlgud-tooltip-echo-area tooltip-use-echo-area (not tooltip-mode))) t))))))) (defun matlab-shell-gud-find-tooltip-expression (event) "Identify an expression to output in a tooltip at EVENT. Unlike `tooltip-expr-to-print', this looks at the symbol, and if it looks like a function call, it will return nil." (interactive) (with-current-buffer (tooltip-event-buffer event) ;; Only do this for MATLAB stuff. (when matlab-shell-gud-minor-mode (let ((point (posn-point (event-end event)))) (if (use-region-p) (when (and (<= (region-beginning) point) (<= point (region-end))) (buffer-substring (region-beginning) (region-end))) ;; This snippet copied from tooltip.el, then modified to ;; detect matlab functions (save-excursion (goto-char point) (let* ((origin (point)) (start (progn (skip-syntax-backward "w_") ;; find full . expression (while (= (preceding-char) ?.) (forward-char -1) (skip-syntax-backward "w_")) (point))) (pstate (syntax-ppss))) (unless (or (looking-at "[0-9]") (nth 3 pstate) (nth 4 pstate)) (goto-char origin) (skip-syntax-forward "w_") (when (> (point) start) ;; At this point, look to see we are looking at (. If so ;; we need to grab that stuff too. (if (not (looking-at "\\s-*(")) (buffer-substring-no-properties start (point)) ;; Also grab the arguments (matlab-forward-sexp) (buffer-substring-no-properties start (point))) ))))))))) (provide 'matlab-shell-gud) ;;; matlab-shell-gud.el ends here ;; LocalWords: el Ludlam eludlam emacsvm eieio defcustom keymap dolist subjob ;; LocalWords: cdr netshell defmacro defun fboundp ebstop ebclear ebstatus ;; LocalWords: ebstack boundp setq realfname progn aset buf noselect dbhotlink ;; LocalWords: COMINT errortext dbhlcmd comint endprompt mello mlg EMACSCAP ;; LocalWords: defclass initarg defmethod longestname namefmt propertize oref ;; LocalWords: newstack nreverse newframe namelen cnt prev MStack BP del NBPS ;; LocalWords: defface bp oset ol eol overlayp MBreakpoints MGUD mlgud's ;; LocalWords: toolboar minibuffer ERRORTXT eventp emacstipstring posn pstate ;; LocalWords: ppss sexp matlab-mode/matlab-load.el0000664000175000017500000003421614611713120016457 0ustar sebastiensebastien;;; matlab-load.el --- automatically extracted autoloads -*- lexical-binding: t -*- ;; ;;; Code: ;;;### (autoloads nil "cedet-matlab" "cedet-matlab.el" (0 0 0 0)) ;;; Generated autoloads from cedet-matlab.el (autoload 'matlab-cedet-setup "cedet-matlab" "\ Setup support for CEDET tools for use with MATLAB." t nil) ;;;*** ;;;### (autoloads nil "company-matlab-shell" "company-matlab-shell.el" ;;;;;; (0 0 0 0)) ;;; Generated autoloads from company-matlab-shell.el (autoload 'company-matlab-shell "company-matlab-shell" "\ A `company-mode' completion backend for `matlab-shell'. \(fn COMMAND &optional ARG &rest IGNORED)" t nil) (register-definition-prefixes "company-matlab-shell" '("company-matlab-shell-")) ;;;*** ;;;### (autoloads nil "linemark" "linemark.el" (0 0 0 0)) ;;; Generated autoloads from linemark.el (autoload 'enable-visual-studio-bookmarks "linemark" "\ Bind the viss bookmark functions to F2 related keys. \\ \\[viss-bookmark-toggle] - To=ggle a bookmark on this line. \\[viss-bookmark-next-buffer] - Move to the next bookmark. \\[viss-bookmark-prev-buffer] - Move to the previous bookmark. \\[viss-bookmark-clear-all-buffer] - Clear all bookmarks." t nil) (register-definition-prefixes "linemark" '("linemark-" "viss-bookmark-")) ;;;*** ;;;### (autoloads nil "matlab" "matlab.el" (0 0 0 0)) ;;; Generated autoloads from matlab.el (autoload 'matlab-is-matlab-file "matlab" "\ Enter `matlab-mode' when file content looks like a MATLAB *.m file or for empty files *.m files when `matlab-mode-for-new-mfiles' indicates as such." nil nil) (add-to-list 'magic-mode-alist '(matlab-is-matlab-file . matlab-mode)) (autoload 'matlab-mode "matlab" "\ MATLAB(R) mode is a major mode for editing MATLAB dot-m files. \\ Convenient editing commands are: \\[matlab-comment-region] - Comment/Uncomment out a region of code. \\[matlab-fill-comment-line] - Fill the current comment line. \\[matlab-fill-region] - Fill code and comments in region. \\[matlab-complete-symbol] - Symbol completion of matlab symbolsbased on the local syntax. Convenient navigation commands are: \\[matlab-beginning-of-command] - Move to the beginning of a command. \\[matlab-end-of-command] - Move to the end of a command. Convenient template insertion commands: \\[tempo-template-matlab-function] - Insert a function definition. \\[tempo-template-matlab-if] - Insert an IF END block. \\[tempo-template-matlab-for] - Insert a FOR END block. \\[tempo-template-matlab-switch] - Insert a SWITCH END statement. \\[matlab-insert-next-case] - Insert the next CASE condition in a SWITCH. \\[matlab-insert-end-block] - Insert a matched END statement. With optional ARG, reindent. \\[matlab-stringify-region] - Convert plain text in region to a string with correctly quoted chars. Variables: `matlab-indent-level' Level to indent blocks. `matlab-continuation-indent-level' Level to indent after ... continuation `matlab-case-indent-level' Level to unindent case statements. `matlab-indent-past-arg1-functions' Regexp of functions to indent past the first argument on continuation lines. `matlab-maximum-indents' List of maximum indents during lineups. `matlab-comment-column' Goal column for on-line comments. `fill-column' Column used in auto-fill. `matlab-indent-function-body' If non-nil, indents body of MATLAB functions. `matlab-functions-have-end' If non-nil, MATLAB functions terminate with end. `matlab-fill-code' Non-nil, auto-fill code in auto-fill-mode. `matlab-fill-strings' Non-nil, auto-fill strings in auto-fill-mode. `matlab-verify-on-save-flag' Non-nil, enable code checks on save. `matlab-vers-on-startup' If t, show version on start-up. `matlab-handle-simulink' If t, enable simulink keyword highlighting. All Key Bindings: \\{matlab-mode-map} \(fn)" t nil) (register-definition-prefixes "matlab" '("gud-matlab-debug-active" "matlab-" "ml-fl-anchor-limit")) ;;;*** ;;;### (autoloads nil "matlab-cgen" "matlab-cgen.el" (0 0 0 0)) ;;; Generated autoloads from matlab-cgen.el (autoload 'matlab-insert-map-fcn "matlab-cgen" "Keymap for C-c C-c in matlab-mode" t 'keymap) (autoload 'matlab-generate-latex "matlab-cgen" "\ Convert a MATLAB M file into a Latex document for printing. Author: Uwe Brauer oub@eucmos.sim.ucm.es Created: 14 Feb 2002" t nil) (register-definition-prefixes "matlab-cgen" '("matlab-")) ;;;*** ;;;### (autoloads nil "matlab-compat" "matlab-compat.el" (0 0 0 0)) ;;; Generated autoloads from matlab-compat.el (register-definition-prefixes "matlab-compat" '("matlab-")) ;;;*** ;;;### (autoloads nil "matlab-complete" "matlab-complete.el" (0 0 ;;;;;; 0 0)) ;;; Generated autoloads from matlab-complete.el (autoload 'matlab-property-function "matlab-complete" "\ Regexp of all builtin functions that take property lists." nil t) (autoload 'matlab-complete-symbol "matlab-complete" "\ Complete a partially typed symbol in a MATLAB mode buffer. \(fn &optional ARG)" t nil) (register-definition-prefixes "matlab-complete" '("matlab-")) ;;;*** ;;;### (autoloads nil "matlab-maint" "matlab-maint.el" (0 0 0 0)) ;;; Generated autoloads from matlab-maint.el (autoload 'matlab-maint-minor-mode "matlab-maint" "\ Minor mode for matlab-mode maintainrs. If called interactively, toggle `Matlab-Maint minor mode'. If the prefix argument is positive, enable the mode, and if it is zero or negative, disable the mode. If called from Lisp, toggle the mode if ARG is `toggle'. Enable the mode if ARG is nil, omitted, or is a positive number. Disable the mode if ARG is a negative number. The mode's hook is called both when the mode is enabled and when it is disabled. \(fn &optional ARG)" t nil) (put 'global-matlab-maint-minor-mode 'globalized-minor-mode t) (defvar global-matlab-maint-minor-mode nil "\ Non-nil if Global Matlab-Maint minor mode is enabled. See the `global-matlab-maint-minor-mode' command for a description of this minor mode. Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization') or call the function `global-matlab-maint-minor-mode'.") (custom-autoload 'global-matlab-maint-minor-mode "matlab-maint" nil) (autoload 'global-matlab-maint-minor-mode "matlab-maint" "\ Toggle Matlab-Maint minor mode in all buffers. With prefix ARG, enable Global Matlab-Maint minor mode if ARG is positive; otherwise, disable it. If called from Lisp, enable the mode if ARG is omitted or nil. Matlab-Maint minor mode is enabled in all buffers where `(lambda nil Should we turn on in this buffer? Only if in the project. (let ((dir \(expand-file-name default-directory)) (ml (file-name-directory \(expand-file-name (locate-library matlab))))) (when (string= ml (substring dir 0 (min (length dir) (length ml)))) (matlab-maint-minor-mode 1))))' would do it. See `matlab-maint-minor-mode' for more information on Matlab-Maint minor mode. \(fn &optional ARG)" t nil) (register-definition-prefixes "matlab-maint" '("matlab-maint-")) ;;;*** ;;;### (autoloads nil "matlab-netshell" "matlab-netshell.el" (0 0 ;;;;;; 0 0)) ;;; Generated autoloads from matlab-netshell.el (autoload 'matlab-netshell-server-active-p "matlab-netshell" "\ Return non-nil if there is an active MATLAB netshell server." nil nil) (autoload 'matlab-netshell-server-start "matlab-netshell" "\ Start the MATLAB netshell server." t nil) (register-definition-prefixes "matlab-netshell" '("matlab-netshell-")) ;;;*** ;;;### (autoloads nil "matlab-publish" "matlab-publish.el" (0 0 0 ;;;;;; 0)) ;;; Generated autoloads from matlab-publish.el (register-definition-prefixes "matlab-publish" '("matlab-")) ;;;*** ;;;### (autoloads nil "matlab-scan" "matlab-scan.el" (0 0 0 0)) ;;; Generated autoloads from matlab-scan.el (register-definition-prefixes "matlab-scan" '("matlab-" "mlf-")) ;;;*** ;;;### (autoloads nil "matlab-shell" "matlab-shell.el" (0 0 0 0)) ;;; Generated autoloads from matlab-shell.el (autoload 'matlab-mode-determine-matlabroot "matlab-shell" "\ Return the MATLABROOT for the 'matlab-shell-command'." nil nil) (autoload 'matlab-shell "matlab-shell" "\ Create a buffer with MATLAB running as a subprocess. MATLAB shell cannot work on the MS Windows platform because MATLAB is not a console application." t nil) (register-definition-prefixes "matlab-shell" '("gud-matlab-marker-regexp-prefix" "matlab-")) ;;;*** ;;;### (autoloads nil "matlab-shell-gud" "matlab-shell-gud.el" (0 ;;;;;; 0 0 0)) ;;; Generated autoloads from matlab-shell-gud.el (autoload 'matlab-shell-mode-gud-enable-bindings "matlab-shell-gud" "\ Enable GUD features for `matlab-shell' in the current buffer." nil nil) (autoload 'matlab-shell-gud-startup "matlab-shell-gud" "\ Configure GUD when a new `matlab-shell' is initialized." nil nil) (autoload 'mlg-reset-breakpoints "matlab-shell-gud" "\ Remove all cached breakpoints." nil nil) (autoload 'matlab-shell-gud-minor-mode "matlab-shell-gud" "\ Minor mode activated when `matlab-shell' K>> prompt is active. This minor mode makes MATLAB buffers read only so simple keystrokes activate debug commands. It also enables tooltips to appear when the mouse hovers over a symbol when debugging. \\ Debug commands are: \\[matlab-shell-gud-mode-edit] - Edit file (toggle read-only) Allows editing file without causing MATLAB to exit debug mode. \\[gud-break] - Add breakpoint (ebstop in FILE at point) \\[gud-remove] - Remove breakpoint (ebclear in FILE at point) \\[gud-list-breakpoints] - List breakpoints (ebstatus) \\[gud-step] - Step (dbstep in) \\[gud-next] - Next (dbstep) \\[gud-finish] - Finish function (dbstep out) \\[gud-cont] - Continue (dbcont) \\[matlab-shell-gud-show-symbol-value] - Evaluate expression \\[mlg-show-stack] - Where am I (ebstack) \\[gud-stop-subjob] - Quit (dbquit) If called interactively, toggle `Matlab-Shell-Gud minor mode'. If the prefix argument is positive, enable the mode, and if it is zero or negative, disable the mode. If called from Lisp, toggle the mode if ARG is `toggle'. Enable the mode if ARG is nil, omitted, or is a positive number. Disable the mode if ARG is a negative number. The mode's hook is called both when the mode is enabled and when it is disabled. \(fn &optional ARG)" t nil) (put 'global-matlab-shell-gud-minor-mode 'globalized-minor-mode t) (defvar global-matlab-shell-gud-minor-mode nil "\ Non-nil if Global Matlab-Shell-Gud minor mode is enabled. See the `global-matlab-shell-gud-minor-mode' command for a description of this minor mode. Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization') or call the function `global-matlab-shell-gud-minor-mode'.") (custom-autoload 'global-matlab-shell-gud-minor-mode "matlab-shell-gud" nil) (autoload 'global-matlab-shell-gud-minor-mode "matlab-shell-gud" "\ Toggle Matlab-Shell-Gud minor mode in all buffers. With prefix ARG, enable Global Matlab-Shell-Gud minor mode if ARG is positive; otherwise, disable it. If called from Lisp, enable the mode if ARG is omitted or nil. Matlab-Shell-Gud minor mode is enabled in all buffers where `(lambda nil Should we turn on in this buffer? Only if in a MATLAB mode. (when \(eq major-mode 'matlab-mode) (matlab-shell-gud-minor-mode 1)))' would do it. See `matlab-shell-gud-minor-mode' for more information on Matlab-Shell-Gud minor mode. \(fn &optional ARG)" t nil) (register-definition-prefixes "matlab-shell-gud" '("gud-matlab-" "matlab-" "mlg-")) ;;;*** ;;;### (autoloads nil "matlab-syntax" "matlab-syntax.el" (0 0 0 0)) ;;; Generated autoloads from matlab-syntax.el (register-definition-prefixes "matlab-syntax" '("matlab-")) ;;;*** ;;;### (autoloads nil "matlab-topic" "matlab-topic.el" (0 0 0 0)) ;;; Generated autoloads from matlab-topic.el (autoload 'matlab-shell-help-mode "matlab-topic" "\ Major mode for viewing MATLAB help text. Entry to this mode runs the normal hook `matlab-shell-help-mode-hook'. Commands: \\{matlab-shell-help-mode-map} \(fn)" t nil) (register-definition-prefixes "matlab-topic" '("matlab-shell-")) ;;;*** ;;;### (autoloads nil "mlint" "mlint.el" (0 0 0 0)) ;;; Generated autoloads from mlint.el (autoload 'mlint-minor-mode "mlint" "\ Toggle mlint minor mode, a mode for showing mlint errors. With prefix ARG, turn mlint minor mode on iff ARG is positive. \\{mlint-minor-mode-map\\} \(fn &optional ARG)" t nil) (register-definition-prefixes "mlint" '("mlint-")) ;;;*** ;;;### (autoloads nil "semantic-matlab" "semantic-matlab.el" (0 0 ;;;;;; 0 0)) ;;; Generated autoloads from semantic-matlab.el (autoload 'semantic-default-matlab-setup "semantic-matlab" "\ Set up a buffer for parsing of MATLAB files." nil nil) (register-definition-prefixes "semantic-matlab" '("matlab-" "semantic-")) ;;;*** ;;;### (autoloads nil "semanticdb-matlab" "semanticdb-matlab.el" ;;;;;; (0 0 0 0)) ;;; Generated autoloads from semanticdb-matlab.el (register-definition-prefixes "semanticdb-matlab" '("matlab-mode" "semantic")) ;;;*** ;;;### (autoloads nil "srecode-matlab" "srecode-matlab.el" (0 0 0 ;;;;;; 0)) ;;; Generated autoloads from srecode-matlab.el (autoload 'srecode-semantic-handle-:matlab "srecode-matlab" "\ Add macros into the dictionary DICT based on the current MATLAB buffer. Adds the following: FILE_SYMBOL - The file name as a symbol. FILE_DOC_SYMBOL - The file name as a symbol for doc strings. PACKAGE - The package this file is in, or empty if none. FILE_CLASS - Show section if filename should be a class. FILE_FUNCTION - Show setion if filename is a function. On class prediction - when filling in an empty file, if the filename and directory it is in match, for example @foo/foo.m then foo should be a classdef. \(fn DICT)" nil nil) ;;;*** ;;;### (autoloads nil "tlc" "tlc.el" (0 0 0 0)) ;;; Generated autoloads from tlc.el (autoload 'tlc-mode "tlc" "\ \(fn)" t nil) (add-to-list 'auto-mode-alist '("\\.tlc\\'" . tlc-mode)) (register-definition-prefixes "tlc" '("tlc-")) ;;;*** ;;;### (autoloads nil nil ("matlab-mode-pkg.el") (0 0 0 0)) ;;;*** (provide 'matlab-load) ;; Local Variables: ;; version-control: never ;; no-byte-compile: t ;; no-update-autoloads: t ;; coding: utf-8 ;; End: ;;; matlab-load.el ends here matlab-mode/dl_emacs_support.m0000775000175000017500000001061514611713120017501 0ustar sebastiensebastien% Copyright (C) 1992-2023 Eric Ludlam (and others) % 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 . function dl_emacs_support(varargin) % Download MATLAB support files for Emacs % % DL_EMACS_SUPPRT - download all Emacs support files into the % current directory. % % DL_EMACS_SUPPORT(FILESET) - download a FILESET of Emacs support. % Sets are: % dl - Download a new version of this download script. % core - Just the core MATLAB support files. % tlc - Just the core MATLAB/TLC support files. % cedet - Core, plus additional support for MATLAB using CEDET support. % Learn more about CEDET at: http://cedet.sf.net % support - Just the build files and READMEs for compiling. % all - All files % % DL_EMACS_SUPPORT(FILESET,DEST) - download FILESET and save in % destination directory DEST % % For the most reliable refresh of the repository, run these two % commands, the frist will make sure the downloader is current. % % dl_emacs_support dl % dl_emacs_support % % On unix, you can then execute: % % !make % % to compile. po = inputParser; addOptional(po, 'fileset', 'all', @ischar) addOptional(po, 'destination', pwd, @ischar) po.parse(varargin{:}); stuff = po.Results; if exist(stuff.destination,'dir') ~= 7 error(['The folder: ''',stuff.destination, ''', does not exist.']); end downloader = { 'dl_emacs_support.m' }; coreFiles = { 'matlab-load.el' 'matlab.el' 'mlint.el' ... 'matlab-publish.el' 'company-matlab-shell.el' ... 'linemark.el' ... 'toolbox/emacsinit.m' 'toolbox/opentoline.m' 'toolbox/emacsdocomplete.m' }; tlcFiles = { 'tlc.el' }; cedetFiles = { 'cedet-matlab.el' 'semantic-matlab.el' ... 'semanticdb-matlab.el' 'srecode-matlab.el' ... 'templates/srecode-matlab.srt' }; supportFiles = { 'README.org' 'INSTALL' 'ChangeLog' ... 'Project.ede' 'Makefile' ... 'toolbox/Project.ede' 'toolbox/Makefile' ... 'templates/Project.ede' 'templates/Makefile'}; switch stuff.fileset case 'dl' getfiles(downloader); case 'core' mktoolboxdir getfiles(coreFiles); case 'tlc' mktoolboxdir getfiles(coreFiles); getfiles(tlcFiles); case 'cedet' mktoolboxdir getfiles(coreFiles); mktemplatedir; getfiles(cedetFiles); case 'support' mktemplatedir; getfiles(supportFiles); case 'all' mktoolboxdir getfiles(coreFiles); getfiles(tlcFiles); mktemplatedir; getfiles(cedetFiles); getfiles(supportFiles); otherwise error('Unknown fileset %s.', stuff.fileset); end function mktemplatedir templateDir = fullfile(stuff.destination,'templates'); if ~exist(templateDir,'dir') mkdir(templateDir); end end function mktoolboxdir toolboxDir = fullfile(stuff.destination,'toolbox'); if ~exist(toolboxDir,'dir') mkdir(toolboxDir); end end function getfiles(fList) for i = 1:length(fList) file = fList{i}; destFullFile = fullfile(stuff.destination,file); [ contents status ] = ... urlread(['https://sourceforge.net/p/matlab-emacs/src/ci/master/tree/cedet-matlab.el?format=raw',... file,'?revision=HEAD']); if ~status fprintf('Unable to download %s.\n', file); else fid = fopen(destFullFile,'w'); fwrite(fid,contents); fclose(fid); fprintf('Successfully downloaded and created: ''%s''.\n',... destFullFile); end end end end matlab-mode/examples/0000775000175000017500000000000014611713120015570 5ustar sebastiensebastienmatlab-mode/examples/matlab-and-org-mode/0000775000175000017500000000000014611713120021277 5ustar sebastiensebastienmatlab-mode/examples/matlab-and-org-mode/sinewave.png0000664000175000017500000004050114611713120023626 0ustar sebastiensebastienPNG  IHDRk|u pHYsgRtIME &}tI$tEXtSoftwareMATLAB, The MathWorks, Inc. IDATxtT7!?&ͰqPPjРH5!nb{V]۶mDzyMs:Io:)V~'A4Цzb{0}f56̇O5I*c!ALH a B$(H0G!`$Hѯ!$H⣂ 9$˚Qa/`$H蔕b<ڐ}"ABHN B$@H\'xHސ /G?"ABwL~e aX]!AB/HH"AЀ!$H`t ,$Hh<!9$Gr %Ë 6̲"AB|zaH" AB3Ih pc0hH#/) H D^DBggt\III- ؆wW\KOO͵l+_2DdUUՆ {۹sg\\\^裏;wηr;v~aB$ bL>nll,--lϟʪݲe˸Ǯ]ܹsoO(,} D" ED֕4ʽcʊxTVV\3"hVfN3gΔdww̝;7gΜ3fz"2j0sh-51s\`ATTT}}}oouzpWU[ooo݋q ̜ m6[AAݻ}.}[N;*rrrDt* KW^yw/`~!ab4"R\\|ԩJ۝ѱw^[RR{XCCC]]]NNNzzoӅjjjOOϝ;|eժU}Ajy@ș > #A"l@h bt0=$B.ц DR A"4@B X mH 4 &@H ˡ $$&<$&nqe$ 1Y$&>qцL B "Ab`mH 1q4 &$&ޕ9ІL D"AB5VfB0$HL H uWmHZ$HF#AB:UfE Ѐ$Hц h@!A"Ht6$ H$H$P 1>RA $B 1:RVC0.$G GD 6$ 0$A!AmH@$HiZ`$$1ц*Z"t\Eeff:ҥK'Ot\SNHNNSA c1ڰarsϞ=yyy;wCV~_Hkkk%#|XZZjϟ?_SSU[[e˖`_f͡C͛TWWرc.[s(6=ȭ[HYYҥKE$%%⮻:xC=4{Ǿk'Oxcbb믿>???薙{v}xKHH(,,G>_gy-$lHHfN---"Quu]"xV)9AHRRߺqy˗x㍷rKzzOӞ=M6$σ|̚5o]91p/"/^\rܹs 8pw}ʝb܌i;9AHbbz~"֖WQQ,.\|FɋgX6$]VΔfbƊHWWߺ,3f(xg|6mڵ"[oT "q9AΙ3GDnߺ&\s5"2m4zΆ9AΜ9SFK"2wǎ<{Q 9A.X **w8oNLLzf66$@̝ m6[AAݻ}.}[̙3+VȮ]?gee}a>h@"|fދ-"ŧNtݹ{z%%% 544䤧N?gϞMNN~Gjt p߿ZDv͛b`6헿ƍO8q eqś6m AE>1}C~#]s5~Slnll,--lϟʪݲeY~T$`n1yܺu-]4&&&%%btRO/;wnӦM4dp3slooonnńB9zh0Ovm*P̜ [ZZD$;;o]I;'ӦM+-- yy@ L MD455 ӧׯ_oQ!9A^|YDf͚>{l |xwwm.\LLmH03_gppPDG>駟zjwm2@Vf+"]]]~J>|ke#0VΔfNsINNpvx%w#i@B|0 3'ș3gh Rp¹s8>x)";v/P6$f+((8rݻqer?~<>>}}w_zzl2gxuTeeػw-))IHH=.''Ǘ 03_RDo{lCCC7o^b֥  mH0 E$%%СC/<111@#lI&A'ڐ`h$Hcc oZ0:$!As@iH4D fhCA =40:HP @3"A#l!$!A H l0"$!AmH00#A@0Y1c!A #l9$!A{h`n @HP @/hCQ 6$!As=Xl010A;!@HzX $HC;#A#lC$HC/x2g$H]c t @hC> u=4?[$Hut:].WRRҢE233<{Ed޼y .9sf8+W44#@o̟ 6l000ܳgO^^Ν;T\\_>\~Lg&b766ldeenٲ%n{)//?rȑ#GJJJ_zOS!;v++===wu'|rرٳgu`gggll5\3|7?88XTTtɅ FshaC0slw-\Ǐ۷nݺ3gV^c%>ڵkԩ-@̼[DO:UYYvsss;::zKJJ|khhIOOWV|'N_|zj]fM7:C&Oc%%%"b7o޼bŊ*Ea?B^*QLҺRQU )iyjA?,$HC`0\4G#l`8$HC8`bdHc  hCVHa 0HP OZ!Aj60.$!A00 aC#A@dрBA6D  mH$dD1+0$!A0@$ #60$!A0 1$a L uH@ $HC`6! Ha$~ HP lOF /F|HP d@XEk]uvv:N˕hѢe LHUUՆ {۹sg\\h)KKKm6[yykjjjkkl4 m1!Ai֭"RVVtҘӧ G`NSDϟQ]#Af ޽۷r?_PPamb N1:uvvttݻ$$$h]f i]~\'+V{`ZZ{]Qz|J ==@RRR:uBA6A@$ B:$HC`!  $HP uHA6L  XmH $ӱ`HP OA!ohGD|$b L uH di$H & uH6$* x A@$c? - uH`h[ d!RHP &=4D  ІbmM$H  H$H0A- 4Z4שּ u. B:;;NJJJZhQfff0G{̛7o…3g s {+DڰarsϞ=yyy;w됦⮮.|pa֔A69 X؍6555YYY[l pTgg{ˏ9rȑ^zSAD^ Ccwv+gJ$c#I IDATȪ*$9s戈{&99ygx$HC\!Adp$HC`ц51H$H CcQ  X직5рH$HCc Bd  ԡ `HP hG`$HPA66 A@$LmH{h0.$L uH0A직)1F0HP $Dc HP E#A HV:!Ad@C#A@$VE` RHP A@:$H9&&Z"t\Eeff}nYdI ` XL%dUUՆ {۹sg\\\>GDZ[[U(XZZjϟ?_SSU[[e˖dӧO_L40Z0'ȭ[HYYҥKcbbRRR***O~K. /s6mJ n Eѣ>CGG?_XXxmP"f9& ED֕4ʽ'?6mZiii8`J a1a&Ommm"p8D)᯿ӧׯ_oT!|/˗Ed֬Y~gvwwo۶m…[46qe}s|Nwv+3yQz}駽^-Ñ0VΔ&I7nT¢";;{"+"]]]~W=Ẻk6,$AVUU)V* rΜ9"vINN onoɑ` =;dy7Ch̙2ZT>pܹc=g}x֬Y3.eW^G#IdLL̨ ,lu)" }cq|'Edǎ^6$IIXl6[AA#Gv+.G۷>Edٲe~Ox9`V&O"R\\|ԩJ۝ѱw^[RR{XCCC]]]NN 0~A"p8 7TWW?c۷oڼy+. ц0FH)"))) _|1bbb#X =` B {6!A`~4Z$H;@h#HP @b B uHo "A6!G:$Hڐ0 LHP FAC 0!Z+$Di4>$HCmІDF H HD ~E<AІD80F  ` " Zb H H1ڐF؈$!AhCbC#A@$mHL HD t@H Hh z~FAZֺt:.+))iѢE{ҥ'O\Sfddddd$''TV!J"x|@HUUU6lPnٳ'//oΝqqq/++;toWZ3f?&++v˖-f͚C͛7gޱcŋ#P6lHv54d֭[Elҥ"RQQq]w???2{v}xKHH(,,G8_gy bhC"0ЖdKKdgg+;i{Gwuu]wu_xc괴4<~wv+3Iܸq˗/X{13f(xg|6mڵ?[o5n$/$.]VΔ&IUUUw366VIsxeMkDdڴi~[7F:ay7r~̙3e-"s G: G#=0N6uTe}QQQt:Edc=7ߜw2l `A&Iclw-\Ǐ۷nݺ3g(7VX!"v"rwGz iI:uvvttݻ$$$PWW9|WϞ=#h.@̟ KJJEno޼Yi1`~_nܸĉ'NP/^iӦ.aB |@W i] q5!Dz:d{ ,ʈ$ 0hCZ H  @?H H  kC $:/ -$ mHa }"AG=#AІ-$/ 9$mH+ =#A/z#A!ц479$:/0$mH #AJ Aц40$/0$mH3 A@$mH$  C3 it4 a,$H0B1 $H!" BXh@ H`dA i4 a\$H0  A ц?04$ @ рё (=L DRh@HD@Ӣ O4 aZ!Nr%%%-Z(333~]r%...===77fT-6m0 K$Ȫ 6 (7ٓsθG]zG=wo;v[1mH`)vccciif+//?|MMMVVVmm-[vseddϟ?yU>իWwvvFxuTeeػw-))IHH=.'''==]Drrro>}OMM9xs򕯬ZJ/B-5k s3t8/))ݾy+V>{^xbFFƦM+ +244u & Y'5EXA`KM8a$H4BdӅu ^CX YJ:HfٓVC!rb $@ DaM$H_0˞7, +1e <<6 +̲Ge b~ #AFG @BiFR^8$`t$H8!? $ B!AG$>Ñ `W C kɵ{Q f$># *X*Dr#0$@H# C$ H#0.$`L"@0H3S sHBLC$P H#0$H@h(!X9L 2J3J$>uSH'3W z"ut[` LaÉG`A{"d"5m:)0d 8| VmK24^xK4.D=Z~^"%XH7;u9`T6#i=@ G?ĈHZZTGv‡BΗ#Ô"P+#A4wʑG i]1( O(y \,~3 Q@Xv=ȰA"q|{!AtI4s$Hٸqfvv5@HRUUx|7cccI ٳoFEY E`8D Ѓ _#(x衇ϟEQ#Arʕ+w}hϤ: uHP uIJNr%%%-Z(33St{"dѝ{y-\p̙Z#wioookkr8\a,MMM&~nk6٥KNh4!SUUaÆJ^^Ν;4J?8p .^VmKҕ⮮.|pCo~kѤ$#[YYzcǎףOeee[ID|iʕO=T$=hll,--MLLܲeK^^^{{MjkklӟTtwmnnNNN7y1ѝN}=,^k_׿ /R||c=uo}+--??ÇN_֯_?}+Wh]mذ!>>~-ܢU1:f͚'OΛ77otүk҅ym۶oqhhjR6 wSSSkjj|+SSS?# ӏ>/Pm=z?v-IHHP~9zvHZZA@rr97[o}KD^u-zvmZQ>guTtt$A@[[p8D$- 'Nx>^ti۶m~ui駟z[lѺz'7nX]]OԩS5Ml[[[^^^EE{_pB9s䜜k4΃ Xy%?%YFGӡN>zB\Z,x?gǎ{>իWk]>\WWW\\̕VۿٳnwmmVUnj3?<3EͶvZy뭷)Kǔ=4{օh)no]CEYjUCCҥKo߮u9zdɒx??Ecʹ/8qBK'**J E{!Mu뭷ʗ3ns=Zעc!|\eܹcxjuTSL~ #Go[UD  DEEl6ߺY|h/488XTTtɅ ̯h]ƞ}Y'|RDv!#Q.e3oNLLϯ^77f67~Z !` v[t\Ǐ/((а6]vqFH]]]~.W^~Ж-[׾(w)7kroiӦKDVX!"v^YY)"w}6ejnnNLL AFqqS*++nwnnnGG޽{^oII.5F||NCѩ}7־իWݻf͚nIt䷿O?}wΜ9k_niiz˗/R,Wccu-Z4{}krzYYXӰ"y?ٳg}iCDs ,ڌ#AؿIIIuuuuu͛7+AD\⻆wk˝"0-Gcǎ _t8>˿hU dƌw_o|cڵ4 }l6/ˍ78q·kś6m"dx7|S,<)CCCZ`*.]t^`cc/**j޼y#?Lwx

1 Overview

Org-mode and matlab-mode provide a very efficient and effective system for creating scientific documents which contain MATLAB code and/or Simulink models along with the results of these. The results of running MATLAB code or simulating Simulink models is placed into the org-mode file by org-mode using org babel. Org babel is org-mode's ability to execute source code within org-mode files and optionally insert the results back in to the org-mode file. You define source code in code blocks, e.g.

#+begin_src LANGUAGE <OPTIONS>
  <CODE>
#+end_src

2 Example

With org-mode you can embed semantically colored code such as MATLAB within your document and semantically edit it using "Org -> Editing -> Edit Source Example" menu or C-c '. For example, here's a MATLAB enumeration class:

classdef WeekDays
   enumeration
       Monday, Tuesday, Wednesday, Thursday, Friday
   end
end

You can use org-mode babel to evaluate MATLAB code blocks. For example, if you type C-c C-c in the code block, org-mode will evaluate the code in the *MATLAB* session and insert the value of ans just below the code block.

a=1+3+5+7;
ans=a
16

You can also use org-mode babel evaluate MATLAB code blocks to plot and insert figures back in to this file. For example:

t=[0:.1:2*pi];
y=sin(t);
plot(t,y);

sinewave.png

3 HTML Export

You need the htmlize package to get coloring in HTML.

We set the "#+html_head_extra" properties above to configure CSS. We used M-x org-html-htmlize-generate-css to create ./css/styles-from-org.css and added our customizations in ./css/styles.css.

4 Setup

The following is known to work with Emacs 27 which includes org-mode 9.3. It is also know to work with Emacs 27 and org-mode 9.6.1.

To use matlab-mode with org-mode, you need to configure org-mode for matlab-mode. See README.org to install matlab-mode.

We configure matlab-mode to use buffer name *MATLAB* and we alter org-mode to use this buffer by adding the following to your ~/.emacs.

;;-----------------------------------------------------------------------------------------;;
;; org babel for matlab - make all matlab code blocks execute in the same *MATLAB* session ;;
;;-----------------------------------------------------------------------------------------;;

;; A better approach to setting org-babel-load-languages is to customize it:
;;   M-x customize-variable RET org-babel-load-languages RET
;; However, we are doing this here to illustrate how matlab interacts with org
(setq org-babel-load-languages '((emacs-lisp . t)
                                 (matlab . t)))

(setq org-babel-default-header-args:matlab '((:session . "*MATLAB*")))

(defun matlab-org-session-advice (orig-fun &rest args)
  "Advice for org to reuse the *MATLAB* buffer"
  ;; ob-octave.el leverages both org-babel-matlab-emacs-link-wrapper-method and
  ;; org-babel-octave-wrapper-method when interacting with the *MATLAB* buffer.
  ;; Here we fix a couple items such as adding cd default-directory:
  (setq org-babel-matlab-emacs-link-wrapper-method
        (concat "\
    cd('" default-directory "');
    %s
    if ~exist('ans','var') ans=''; end
    if ischar(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid);
    else, save -ascii %s ans
    end
    delete('%s')
    "))
  (setq org-babel-octave-wrapper-method
        (concat "\
    cd('" default-directory "');
    %s
    if ~exist('ans','var') ans=''; end
    if ischar(ans) || isstring(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid);
    else, dlmwrite('%s', ans, '\\t')
    end"))
  (apply orig-fun args))

(defun matlab-org-fixup-print (orig-fun session body result-type &optional matlabp)
  "Fixup figure print to make it work with MATLAB"
  ;; org 9.3 correctly does:     print -dpng figure.png
  ;; org 9.6.1 incorrectly does: print -dpng "figure.png"
  ;; and thus 9.6.1 creates on disk a file name containing quotes which is incorrect, so this
  ;; advice fixes that.
  (setq body (replace-regexp-in-string "^\\(print -dpng \\)\"\\([^\"]+\\)\"" "\\1\\2"  body t))
  (funcall orig-fun session body result-type matlabp))

(defun org-export-dispatch-no-babel-advice (orig-fun &rest args)
  "Instruct babel to not evaluate code blocks (and hence no
prompt) during export, e.g. conversion of org to say html."
  (let* ((org-babel-default-header-args
          (cons '(:eval . "never-export") org-babel-default-header-args))
         (result (apply orig-fun args)))
    result))

(eval-after-load 'ox
  '(progn
     ;; Make C-c C-e `org-export-dispatch' work without prompting to evaluate code blocks
     (advice-add 'org-export-dispatch :around #'org-export-dispatch-no-babel-advice)))

;; org babel for matlab - make all matlab code blocks execute in the same *MATLAB* session
(eval-after-load "org"
  '(progn
     (advice-add 'org-babel-octave-evaluate-external-process :around #'matlab-org-session-advice)
     (advice-add 'org-babel-octave-evaluate-session :around #'matlab-org-session-advice)
     (advice-add 'org-babel-octave-evaluate :around #'matlab-org-fixup-print)))

To try this using "stock" emacs, place the above in org-matlab-setup.el and use:

emacs -q -L /path/to/matlab-mode -l /path/to/matlab-load.el -l org-matlab-setup.el

Author: John Ciolfi

Created: 2023-04-10 Mon 15:58

Validate

matlab-mode/examples/matlab-and-org-mode/matlab-and-org-mode.org0000664000175000017500000001601414611713120025521 0ustar sebastiensebastien# // File: matlab-emacs-src/examples/matlab-and-org-mode/matlab-and-org-mode.org # // Abstract: # // Use this as a template for creating org-files with MATLAB and other language code blocks #+startup: showall #+startup: inlineimages // C-c C-x C-v to toggle, C-c C-x C-M-v to redisplay #+startup: latexpreview // C-c C-x C-l to toggle #+html_head_extra: #+html_head_extra: #+options: ^:{} #+options: toc:nil #+latex_header: \usepackage[margin=0.5in]{geometry} #+latex_header: \usepackage{parskip} #+latex_header: \usepackage{tocloft} #+latex_header: \advance\cftsecnumwidth 0.5em\relax #+latex_header: \advance\cftsubsecindent 0.5em\relax #+latex_header: \advance\cftsubsecnumwidth 0.5em\relax * Overview [[https://orgmode.org/][Org-mode]] and [[https://sourceforge.net/projects/matlab-emacs/][matlab-mode]] provide an efficient and effective system for creating scientific documents which contain MATLAB code and/or Simulink models along with the results of these. The results of running MATLAB code or simulating Simulink models is placed into the org-mode file by org-mode using org babel. [[https://orgmode.org/worg/org-contrib/babel/][Org babel]] is org-mode's ability to execute source code within org-mode files and optionally insert the results back in to the org-mode file. You define source code in code blocks, e.g. : #+begin_src LANGUAGE : : #+end_src * Example With org-mode you can embed semantically colored code such as MATLAB within your document and semantically edit it using "Org -> Editing -> Edit Source Example" menu or =C-c '=. For example, here's a MATLAB [[https://www.mathworks.com/help/matlab/enumeration-classes.html][enumeration class]]: #+begin_src matlab classdef WeekDays enumeration Monday, Tuesday, Wednesday, Thursday, Friday end end #+end_src You can use org-mode babel to evaluate MATLAB code blocks. For example, if you type =C-c C-c= in the following code block, org-mode will evaluate the code in the =*MATLAB*= session and insert the value of =ans= just below the code block. #+begin_src matlab :exports both :results verbatim a=1+3+5+7; ans=a #+end_src #+RESULTS: : 16 You can also use org-mode babel evaluate MATLAB code blocks to plot and insert figures back in to this file. For example: #+begin_src matlab :file sinewave.png :exports both :results file graphics t=[0:.1:2*pi]; y=sin(t); plot(t,y); #+end_src #+RESULTS: [[file:sinewave.png]] * Export to HTML, PDF, etc. You need the htmlize package to get coloring. For HTML export we set the "#+html_head_extra" properties above to configure CSS. We used ~M-x org-html-htmlize-generate-css~ to create ./css/styles-from-org.css and added our customizations in ./css/styles.css. For PDF export we configured several "#+latex_header" properties above. After this setup, you can use the "Org -> Export/Publish" or ~C-c C-e~ to export to HTML, PDF, etc. See the example *.html in this directory. * Setup The following is known to work with Emacs 27 which includes org-mode 9.3. It is also know to work with Emacs 27 and org-mode 9.6.1. To use matlab-mode with org-mode, you need to configure org-mode for matlab-mode. See [[file:../../README.org][README.org]] to install matlab-mode. We configure matlab-mode to use buffer name =*MATLAB*= and we alter org-mode to use this buffer by adding the following to your =~/.emacs=. #+begin_src emacs-lisp ;;-----------------------------------------------------------------------------------------;; ;; org babel for matlab - make all matlab code blocks execute in the same *MATLAB* session ;; ;;-----------------------------------------------------------------------------------------;; ;; A better approach to setting org-babel-load-languages is to customize it: ;; M-x customize-variable RET org-babel-load-languages RET ;; However, we are doing this here to illustrate how matlab interacts with org (setq org-babel-load-languages '((emacs-lisp . t) (matlab . t))) (setq org-babel-default-header-args:matlab '((:session . "*MATLAB*"))) (defun matlab-org-session-advice (orig-fun &rest args) "Advice for org to reuse the *MATLAB* buffer" ;; ob-octave.el leverages both org-babel-matlab-emacs-link-wrapper-method and ;; org-babel-octave-wrapper-method when interacting with the *MATLAB* buffer. ;; Here we fix a couple items such as adding cd default-directory: (setq org-babel-matlab-emacs-link-wrapper-method (concat "\ cd('" default-directory "'); %s if ~exist('ans','var') ans=''; end if ischar(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid); else, save -ascii %s ans end delete('%s') ")) (setq org-babel-octave-wrapper-method (concat "\ cd('" default-directory "'); %s if ~exist('ans','var') ans=''; end if ischar(ans) || isstring(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid); else, dlmwrite('%s', ans, '\\t') end")) (apply orig-fun args)) (defun matlab-org-fixup-print (orig-fun session body result-type &optional matlabp) "Fixup figure print to make it work with MATLAB" ;; org 9.3 correctly does: print -dpng figure.png ;; org 9.6.1 incorrectly does: print -dpng "figure.png" ;; and thus 9.6.1 creates on disk a file name containing quotes which is incorrect, so this ;; advice fixes that. (setq body (replace-regexp-in-string "^\\(print -dpng \\)\"\\([^\"]+\\)\"" "\\1\\2" body t)) (funcall orig-fun session body result-type matlabp)) (defun org-export-dispatch-no-babel-advice (orig-fun &rest args) "Instruct babel to not evaluate code blocks (and hence no prompt) during export, e.g. conversion of org to say html." (let* ((org-babel-default-header-args (cons '(:eval . "never-export") org-babel-default-header-args)) (result (apply orig-fun args))) result)) (eval-after-load 'ox '(progn ;; Make C-c C-e `org-export-dispatch' work without prompting to evaluate code blocks (advice-add 'org-export-dispatch :around #'org-export-dispatch-no-babel-advice))) ;; org babel for matlab - make all matlab code blocks execute in the same *MATLAB* session (eval-after-load "org" '(progn (advice-add 'org-babel-octave-evaluate-external-process :around #'matlab-org-session-advice) (advice-add 'org-babel-octave-evaluate-session :around #'matlab-org-session-advice) (advice-add 'org-babel-octave-evaluate :around #'matlab-org-fixup-print))) #+end_src To try this using "stock" emacs, place the above in org-matlab-setup.el and use: : emacs -q -L /path/to/matlab-mode -l /path/to/matlab-load.el -l org-matlab-setup.el # LocalWords: showall inlineimages latexpreview usepackage parskip tocloft cftsecnumwidth sinewave # LocalWords: cftsubsecindent cftsubsecnumwidth setq defun isstring fixup matlabp dpng funcall # LocalWords: progn htmlize matlab-mode/examples/matlab-and-org-mode/css/0000775000175000017500000000000014611713120022067 5ustar sebastiensebastienmatlab-mode/examples/matlab-and-org-mode/css/styles-from-org.css0000664000175000017500000024766614611713120025677 0ustar sebastiensebastien matlab-mode/examples/matlab-and-org-mode/css/styles.css0000664000175000017500000000124314611713120024124 0ustar sebastiensebastien/* * Used by ../*.org via * #+HTML_HEAD_EXTRA: */ body { font-size: 11pt; text-align: left; line-height:1.2em; } h1 {font-size: 14pt;} .title { padding-bottom: 7px; margin-bottom: 20px; border-bottom: 1px solid #222; } h2 { font-size: 12pt; padding-bottom: 4px; margin-top: 5px; margin-bottom: 5px; border-bottom: 1px solid #DDD; } h3 {font-size: 11pt; color: #0000ff;} h4 {font-size: 9pt; color: #a34d32;} a {text-decoration: none; color: #537d7b} a:visited {text-decoration: none; color: #98855b} a:hover {text-decoration: underline; color: #a34d32} matlab-mode/srecode-matlab.el0000664000175000017500000000610614611713120017161 0ustar sebastiensebastien;;; srecode-matlab.el --- Extra SRecode support for MATLAB ;; ;; Copyright (C) 2014, 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; X-RCS: $Id$ ;; ;; 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 http://www.gnu.org/licenses/. ;;; Commentary: ;; ;; Support some extra dictionary entries for MATLAB code. (require 'srecode) (require 'srecode/dictionary) (require 'srecode/semantic) (require 'matlab) ;;; Code: ;;; :matlab ARGUMENT HANDLER ;; ;; When a :matlab argument is required, fill the dictionary info ;; with this MATLAB specific stuff. ;; ;; Error if not in MATLAB mode. ;;;###autoload (defun srecode-semantic-handle-:matlab (dict) "Add macros into the dictionary DICT based on the current MATLAB buffer. Adds the following: FILE_SYMBOL - The file name as a symbol. FILE_DOC_SYMBOL - The file name as a symbol for doc strings. PACKAGE - The package this file is in, or empty if none. FILE_CLASS - Show section if filename should be a class. FILE_FUNCTION - Show setion if filename is a function. On class prediction - when filling in an empty file, if the filename and directory it is in match, for example @foo/foo.m then foo should be a classdef." (when (not (eq major-mode 'matlab-mode)) (error "Wrong mode for :matlab argument")) (let* ((fsym (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) (dir (directory-file-name (file-name-directory (buffer-file-name)))) (dsym (file-name-nondirectory dir))) ;; Convert the file name to a symbol. (srecode-dictionary-set-value dict "FILE_SYMBOL" fsym) (srecode-dictionary-set-value dict "FILE_DOC_SYMBOL" (upcase fsym)) ;; Check that dsym and fsym are the same (if (string= dsym (concat "@" fsym)) (srecode-dictionary-show-section dict "FILE_CLASS") (srecode-dictionary-show-section dict "FILE_FUNCTION")) ;; Now look at the stack of directories to see what the package name ;; might be. (let ((package "") (next (file-name-nondirectory dir))) (setq dir (file-name-directory dir)) ;; Keep scanning for packages while there is a + in the path. (while (and (> (length dir) 0) (string-match "/\\+\\w" dir)) (let ((first (aref next 0)) (rest (substring next 1))) (if (= first ?+) (setq package (concat rest "." package)) (setq dir "")) ;; Setup for next iteration. (setq dir (directory-file-name (directory-file-name dir))) (setq next (file-name-nondirectory dir)) )) (srecode-dictionary-set-value dict "PACKAGE" package) ))) (provide 'srecode-matlab) ;;; srecode-matlab.el ends here matlab-mode/semantic-matlab.el0000664000175000017500000007221414611713120017343 0ustar sebastiensebastien;;; semantic-matlab.el --- Semantic details for MATLAB files ;;; Copyright (C) 2004-2020 Eric M. Ludlam ;; Author: Eric M. Ludlam ;; This file is not part of GNU Emacs. ;; This 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, or (at your option) ;; any later version. ;; This software 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Parse a MATLAB M file for use w/ CEDET/Semantic ;; ;; The MATLAB language is pretty simple from a functional standpoint in that ;; you can only declare functions. In addition, the language itself is not ;; expressable in a yacc style grammar. It is therefore more expedient ;; to scan for regular expressions. (require 'mode-local) (require 'semantic) (eval-and-compile (condition-case nil (require 'semantic-format) (error (require 'semantic/format))) (condition-case nil (require 'semantic-dep) (error (require 'semantic/dep))) ) (require 'matlab) (require 'matlab-shell) (require 'semanticdb-matlab) ;;; Code: ;;; Utilities ;; ;; These functions wrap behavior provided by semantic for use in parts of matlab mode ;; that need them. Take care of users who don't have cedet/semantic enabled by default. (defun matlab-semantic-get-local-functions-for-script (&optional buffer) "Return the list of functions (as semantic tags) for BUFFER. If `semantic-mode' is not enabled, do something hacky to make it work." (save-excursion (when buffer (set-buffer buffer)) (let ((tags (save-excursion (semantic-refresh-tags-safe) (semantic-find-tags-by-class 'function (current-buffer))))) (when (and (not tags) (not semantic-mode)) ;; We got no tags, and semantic isn't enabled. ;; Lets fake it. ;; call the parse region function. It won't be cached, so we have to do the work every ;; time, but hopefully it is fast enough for matlab-shell and cell scripts. (setq tags (semantic-matlab-parse-region)) ) tags))) (defun matlab-semantic-tag-text (tag buffer) "Return the text string for TAG in BUFFER." (with-current-buffer buffer (buffer-substring-no-properties (semantic-tag-start tag) (semantic-tag-end tag)))) ;;; Configuration ;; (defvar semantic-matlab-system-paths-include '("toolbox/matlab/funfun" "toolbox/matlab/general") "List of include paths under `semantic-matlab-root-directory'. These paths will be parsed recursively by semantic. Class and private directories will be omitted here.") (defvar semantic-matlab-root-directory (matlab-mode-determine-matlabroot) "Root directory of MATLAB installation. Use `semantic-matlab-system-paths-include' to let semantic know which system directories you would like to include when doing completions.") (defun semantic-matlab-root-directory () "Calculate the current MATLAB root directory." (if (matlab-shell-active-p) (matlab-shell-matlabroot) semantic-matlab-root-directory)) ;;; File type detection ;; (defvar semantic-matlab-match-filetype-re "^\\s-*\\(classdef\\|function\\)\\>" "Regexp to identify if a file represents a class or a function.") (defun semantic-matlab-guess-buffer-type (&optional buffer) "Guess what kind of MATLAB content BUFFER contains. Looks @ first declaration to determine if it is a class or function." (save-excursion (if buffer (set-buffer buffer)) (goto-char (point-min)) (if (re-search-forward semantic-matlab-match-filetype-re nil t) (let ((key (match-string 1))) (cond ((string= key "classdef") 'class) ((string= key "function") 'function))) 'script))) ;;; TAG MATCHING ;; ;; CLASS Defintions (defvar semantic-matlab-match-classdef-re "^\\s-*classdef\\b\\s-*\\(?:([^\n)]+)\\)?\\s-*\\<\\(?2:\\w+\\)\\>" "Expression to match a class definition start.") (defun semantic-matlab-class-tags (&optional buffer) "Find the MATLAB class tag, and all methods (functions) in BUFFER. Return argument is: (START END NAME BASECLASSES DOCSTRING METHODS PROPS LOCALFCN)." (save-excursion (if buffer (set-buffer buffer)) (let ((re semantic-matlab-match-classdef-re) start cn end doc base meth (taglist nil) ) (goto-char (point-min)) (when (re-search-forward re nil t) (setq start (match-beginning 0) cn (buffer-substring-no-properties (match-beginning 2) (match-end 2)) base (save-excursion (let ((tmp nil)) (while (looking-at "\\s-*[<&]\\s-*\\(\\(\\sw\\|\\.\\)+\\)") (setq tmp (cons (match-string-no-properties 1) tmp)) (goto-char (match-end 0))) (nreverse tmp))) doc (save-excursion (forward-line) (beginning-of-line) ;; snarf doc string (cond ;; Mathworks standard ((looking-at "%[A-Z0-9_]+\\s-+\\(.*\\)\\s-*$") (match-string-no-properties 1)) ;; lookfor string ((looking-at "%\\s-+\\(.*\\)\\s-*$") (match-string-no-properties 1)) ;; otherwise simply snarf first line of ;; comments under function declaration (t (re-search-forward "[^[:blank:][:cntrl:]]" nil t) (backward-char) (if (looking-at "%\\s-+\\(.*\\)") (match-string-no-properties 1) nil)))) end (save-excursion (goto-char start) (if matlab-functions-have-end (condition-case nil ;; If we get a failure, we should at least ;; return whatever we got so far. (matlab-forward-sexp) (error (point-max))) (matlab-end-of-defun)) (point)) meth (semantic-matlab-sort-raw-function-tags (semantic-matlab-function-tags) end) ) (semantic-matlab-methods-update-tags (car meth) start end) (setq taglist (cons (list start end cn base doc (car meth) (semantic-matlab-properties-tags start end) (car (semantic-matlab-sort-raw-function-tags (car (cdr meth)) (point-max))) ) taglist)) ) (nreverse taglist)))) (defvar semantic-matlab-match-methods-block-re "^\\s-*\\bmethods\\b" "Regular expression for matching the start of a properties block.") (defun semantic-matlab-methods-update-tags (rawtags start end) "Create a tags list out of RAWTAGS and properties found between START and END." (save-excursion (goto-char start) (let ((taglist nil) (tmpend nil) (attrs nil) ) (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) (setq tmpend (point))) (setq attrs (semantic-matlab-parse-attributes-and-move)) (while (and rawtags (< (nth 5 (car rawtags)) tmpend)) (while attrs (semantic-tag-put-attribute (car rawtags) (car attrs) (car (cdr attrs))) (setq attrs (cdr (cdr attrs)))) (setq rawtags (cdr rawtags)))) (goto-char tmpend) ) )) (defvar semantic-matlab-match-properties-block-re "^\\s-*\\bproperties\\b" "Regular expression for matching the start of a properties block.") (defun semantic-matlab-properties-tags (start end) "Create a tags list out of properties found between START and END." (save-excursion (save-match-data (goto-char start) (let ((taglist nil) (tmpend nil) (attrs nil) ) (while (re-search-forward semantic-matlab-match-properties-block-re nil end) (setq attrs (semantic-matlab-parse-attributes-and-move)) (save-excursion ;; find end of properties block (matlab-forward-sexp t) (beginning-of-line) (setq tmpend (point))) (while (re-search-forward "^\\s-*\\(\\w+\\)\\>" tmpend t) (setq taglist (cons (append (apply #'semantic-tag-new-variable (match-string-no-properties 1) nil nil attrs) (list (match-beginning 1) (point-at-eol))) taglist))) (goto-char tmpend) ) (nreverse taglist) )))) (defun semantic-matlab-parse-attributes-and-move () "Parse the properties or method attributes block, and move cursor to end of list." (when (looking-at "\\s-*(") (let ((end (save-excursion (matlab-forward-sexp) (point))) (attrs nil) (case-fold-search t) ) (save-excursion ;; Protection (when (re-search-forward "\\" "Expression to match a function start line.") (defun semantic-matlab-function-tags (&optional buffer) "Find all MATLAB function tags in BUFFER. Return argument is: (START END RETURNVARS NAME ARGUMENTS DOCSTRING). Note that builtin functions from MATLAB will always return START=END=0 and no arguments or return values." (save-excursion (if buffer (set-buffer buffer)) (let ((re semantic-matlab-match-function-re) start ret fn arg end doc (taglist nil) ) (goto-char (point-min)) (if (and (string-match (format "^%s" (semantic-matlab-root-directory)) (buffer-file-name)) (looking-at "%\\([A-Z0-9_]+\\)\\s-+\\(.*\\)\\s-*$")) ;; This is a builtin function, ie there's no function line. ;; Hence we must use function name from the doc string. ;; FIXME ;; How can we get function arguments/return vals for builtin func's? (setq taglist (cons (list 0 0 nil (downcase (match-string-no-properties 1)) nil (match-string-no-properties 2) t ) taglist)) ;; this is a either not builtin or a user function (while (re-search-forward re nil t) (setq start (match-beginning 0) ret (buffer-substring-no-properties (match-beginning 2) (match-end 2)) fn (buffer-substring-no-properties (match-beginning 3) (match-end 3)) arg (buffer-substring-no-properties (match-end 3) (save-excursion (matlab-end-of-command) (point))) doc (save-excursion (forward-line) (beginning-of-line) ;; snarf doc string (cond ;; Mathworks standard ((looking-at "%[A-Z0-9_]+\\s-+\\(.*\\)\\s-*$") (match-string-no-properties 1)) ;; lookfor string ((looking-at "%\\s-+\\(.*\\)\\s-*$") (match-string-no-properties 1)) ;; otherwise simply snarf first line of ;; comments under function declaration (t (re-search-forward "[^[:blank:][:cntrl:]]" nil t) (backward-char) (if (looking-at "%\\s-+\\(.*\\)") (match-string-no-properties 1) nil)))) end (save-excursion (goto-char start) (if matlab-functions-have-end (condition-case nil ;; If we get a failure, we should at least ;; return whatever we got so far. (matlab-forward-sexp) (error (point-max))) (matlab-end-of-defun)) (point))) (setq taglist (cons (list start end (split-string ret "[][,=. \t\n]+" t) fn (split-string arg "[(), \n\t.]+" t) doc nil ) taglist)))) (nreverse taglist)))) ;; TODO - commented out the below. See if anything breaks, then delete. ;; (defun semantic-matlab-parse-oldstyle-class (tags &optional buffer) ;; "Check if BUFFER with current TAGS is the constructor of a class. ;; If this is the case, retrieve attributes from the buffer and scan ;; the whole directory for methods. The function returns a single tag ;; describing the class. This means that in semantic-matlab, the ;; old-style MATLAB classes are linked to the constructor file." ;; (let* ((name (buffer-file-name buffer)) ;; class method methods retval attributes) ;; (when (string-match ".*/@\\(.*?\\)/\\(.*?\\)\\.m" name) ;; ;; this buffer is part of a class - check ;; (setq class (match-string 1 name)) ;; (setq method (match-string 2 name)) ;; (when (string= class method) ; is this the constructor? ;; ;; get attributes of the class ;; ;; TODO - we blindly assume the constructor is correctly defined ;; (setq retval (semantic-tag-get-attribute (car tags) :return)) ;; (goto-char (point-min)) ;; ;; search for attributes ;; (while (re-search-forward ;; (concat "^\\s-*" (car retval) ;; "\\.\\([A-Za-z0-9_]+\\)\\s-*=\\s-*\\(.+\\);") ;; nil t) ;; (push (list (match-string-no-properties 1) ; name ;; (match-string-no-properties 2)) ; default value ;; attributes)) ;; ;; now scan the methods ;; (dolist (cur (delete class ;; (nthcdr 2 ;; (assoc class semanticdb-matlab-user-class-cache)))) ;; (push ;; (semantic-tag-put-attribute ;; (car (semanticdb-file-stream ;; (concat ;; (file-name-directory name) ;; cur ".m"))) ;; :typemodifiers '("public")) ;; methods)) ;; ;; generate tag ;; (semantic-tag-new-type ;; class ;; "class" ;; (append ;; (mapcar (lambda (cur) ;; (semantic-tag-new-variable ;; (car cur) nil (cdr cur) ;; :typemodifiers '("public"))) ;; attributes) ;; methods) ;; nil ;; :typemodifiers '("public")))))) ;; (defun semantic-matlab-find-oldstyle-classes (files) ;; "Scan FILES for old-style Matlab class system. ;; Returns an alist with elements (CLASSNAME LOCATION METHODS)." ;; (let (classes temp tags) ;; (dolist (cur files) ;; ;; scan file path for @-directory ;; (when (string-match "\\(.*\\)/@\\(.*?\\)/\\(.*?\\)\\.m" cur) ;; (if (setq temp ;; (assoc (match-string 2 cur) classes)) ;; (nconc temp `(,(match-string 3 cur))) ;; (push `( ,(match-string 2 cur) ,(match-string 1 cur) ;; ,(match-string 3 cur)) classes)))) ;; classes)) ;;; BEGIN PARSER ;; (defun semantic-matlab-parse-region (&rest ignore) "Parse the current MATLAB buffer for function definitions. IGNORE any arguments which specify a subregion to parse. Each tag returned is a semantic FUNCTION tag. See `semantic-tag-new-function'." (semanticdb-matlab-cache-files) (let* ((bt (semantic-matlab-guess-buffer-type)) (raw (condition-case nil ;; Errors from here ought not to be propagated. (cond ((eq bt 'class) (semantic-matlab-parse-class)) ((eq bt 'function) (semantic-matlab-parse-functions)) (t nil)) (error nil))) tags ctags) (setq tags (mapcar 'semantic-matlab-expand-tag raw)) ;; check if this is a class constructor ;; (setq ctags (list (semantic-matlab-parse-oldstyle-class tags))) ;;(if (car ctags) ctags tags)) (defun semantic-matlab-parse-changes () "Parse all changes for the current MATLAB buffer." ;; NOTE: For now, just schedule a full reparse. ;; To be implemented later. (semantic-parse-tree-set-needs-rebuild)) (define-mode-local-override semantic-tag-components-with-overlays matlab-mode (tag) "Return the list of subfunctions, or class members in TAG." (or (semantic-tag-get-attribute tag :members) (semantic-tag-get-attribute tag :subfunctions))) (defun semantic-matlab-expand-tag (tag) "Expand the MATLAB function tag TAG." (let ((chil (semantic-tag-components-with-overlays tag))) (if chil (semantic-tag-put-attribute tag :members (mapcar 'semantic-matlab-expand-tag chil))) (car (semantic--tag-expand tag)))) (defun semantic-matlab-parse-class (&optional limit) "Parse the class from the current MATLAB buffer up to LIMIT." (semantic-matlab-sort-raw-class-tags (semantic-matlab-class-tags))) (defun semantic-matlab-sort-raw-class-tags (tag-list) "Return a split list of tags from TAG-LIST before END." (let ((newlist nil)) (dolist (tag tag-list) (let ((start (car tag)) (end (nth 1 tag)) (name (nth 2 tag)) (base (nth 3 tag)) (doc (nth 4 tag)) (meth (nth 5 tag)) (props (nth 6 tag)) (local (nth 7 tag))) (setq newlist (cons (append (semantic-tag-new-type name "class" (append props meth) (list base) :documentation doc) (list start end)) newlist)) (setq newlist (append newlist local)) )) newlist)) (defun semantic-matlab-parse-functions (&optional limit) "Parse all functions from the current MATLAB buffer up to LIMIT." (car (semantic-matlab-sort-raw-function-tags (semantic-matlab-function-tags) (or limit (point-max))) )) (defun semantic-matlab-sort-raw-function-tags (tag-list &optional end) "Return a split list of tags from TAG-LIST before END. Return list is: (TAGS-BEFORE-END REMAINING-TAGS)" (let ((newlist nil) (rest tag-list)) ;; Loop until there are no more tags, or no tags before END. (while (and tag-list (> end (car (car tag-list)))) (let* ((tag (car tag-list)) (start (car tag)) (end (nth 1 tag)) (ret (nth 2 tag)) (name (nth 3 tag)) (args (nth 4 tag)) (doc (nth 5 tag)) (builtin (nth 6 tag)) (parts (semantic-matlab-sort-raw-function-tags (cdr tag-list) end)) (chil (car parts))) (setq rest (car (cdr parts))) (setq newlist (cons (append (semantic-tag-new-function name nil args :return ret :subfunctions chil :documentation doc :builtin builtin) (list start end)) newlist)) (setq tag-list rest))) (list (nreverse newlist) tag-list))) ;; The following function tries to parse MATLAB variable ;; assignments. There are only three categories of types: doubles, ;; structs, and classes. It returns a list with elements ;; (NAME TYPE ATTRIBUTES), for example: ;; ("astruct" "struct" "field1" "field2" "field3") ;; ("aclass" "class" "exampleclass") ;; ("anumber" "double" "1356") ;; Of course we can't parse things we don't know, e.g. ;; if (a==5) variable=aclass; else variable=anotherclass; ;; In these cases, the latter assignment counts. ;; Also, we don't know return types of functions (yet...) - the parser ;; will always think it's "double". You can override the ;; parser with a special comment, like: %type% avariable = aclass ;; One day we might use a grammar for this...? (defconst semantic-matlab-type-hint-string "%type%" "Comment string which prefixes a type hint for the parser.") (defun semantic-matlab-parse-assignments () "Parse assignments in current buffer. This function starts at current point and goes backwards, until it reaches a function declaration or the beginning of the buffer. It returns a list of variable assignments (NAME TYPE ATTRIBUTES), where NAME is unique." (let ((limit (or (save-excursion (if (re-search-backward semantic-matlab-match-function-re nil t) (progn (forward-line 1) (point)) nil)) (point-min))) vars) ;; don't parse current line (beginning-of-line) (while (re-search-backward (concat "^\\(" (regexp-quote semantic-matlab-type-hint-string) "\\)?\\([^%]*[^=><~]\\)=\\([^=].*\\)$") limit t) (let ((left (match-string-no-properties 2)) (right (match-string-no-properties 3)) temp) ;; first we have to deal with elipsis... (save-excursion (while (string-match (concat "\\(.*\\)" (regexp-quote matlab-elipsis-string) "\\s-*$") right) (forward-line 1) (setq right (concat (match-string 1 right) (progn (looking-at "^.*$") (match-string-no-properties 0)))))) (save-excursion (while (and (not (bobp)) (progn (forward-line -1) (looking-at (concat "\\(.*\\)" (regexp-quote matlab-elipsis-string) "\\s-*$")))) (setq left (concat (match-string-no-properties 1) left)))) ;; remove bracket expressions and beginning/trailing whitespaces on left-hand side (while (or (string-match "\\((.*)\\|{.*}\\)" left) (string-match "^\\(\\s-+\\)" left) (string-match "\\(\\s-+\\)$" left)) (setq left (replace-match "" t t left))) ;; deal with right-hand side (cond ;; special case: a = set(class,attribute,value) ((string-match "\\s-*set(\\s-*\\([A-Za-z_0-9 ]+\\)\\s-*," right) (setq right (match-string 1 right))) ;; method call which returns same class: class=method(class [,args]) ((and (string-match "\\s-*[A-Za-z_0-9 ]+\\s-*(\\s-*\\([A-Za-z_0-9 ]+\\)\\s-*\\(,\\|)\\)" right) (string= left (match-string 1 right))) (setq right (match-string 1 right))) ;; otherwise reduce right-hand side to first symbol (t (string-match "[[({ ]*\\([A-Za-z_0-9]*\\)" right) (setq right (match-string 1 right)))) (cond ;; multiple assignment, e.g. [a,b]=size(A); ((string-match "\\[\\(.*\\)\\]" left) (dolist (cur (split-string (match-string 1 left) ",")) (string-match "\\s-*\\([A-Za-z_0-9]+\\)\\s-*" cur) (setq cur (match-string 1 cur)) (unless (assoc cur vars) ;; since we don't know any return types, we just say it's double (push (list cur "double" "") vars)))) ;; (nested) structure ((string-match "\\([A-Za-z_0-9.]+\\)\\.\\([A-Za-z_0-9]+\\)" left) (while (string-match "\\([A-Za-z_0-9.]+\\)\\.\\([A-Za-z_0-9]+\\)" left) (let ((name (match-string 1 left)) (field (match-string 2 left))) (if (setq temp (assoc name vars)) (unless (member field temp) (nconc temp (list field))) (push (list name "struct" field) vars)) (setq left name)))) ;; class ((assoc right semanticdb-matlab-user-class-cache) (string-match "\\([A-Za-z_0-9]+\\)\\s-*$" left) (setq left (match-string 1 left)) (if (and (setq temp (assoc left vars)) (string= (nth 1 temp) "struct")) ;; we first thought it's a structure, but it's probably a ;; new-style class (setcdr temp `("class" ,right)) (unless temp (push `(,left "class" ,right) vars)))) (t ;; default is double (string-match "\\([A-Za-z_0-9]+\\)\\s-*$" left) (setq left (match-string 1 left)) (unless (or (assoc left vars) (string= left right)) ; self assignment (push `(,left "double" ,right) vars))) ))) vars)) (define-mode-local-override semantic-get-local-variables matlab-mode (&optional point) "Return a list of local variables for POINT." (semanticdb-matlab-cache-files) (save-excursion (let ((vars (semantic-matlab-parse-assignments)) knowntypes tags) (dolist (cur vars) ;; check right-hand side for known types which might be ;; assigned this variable (if (string= (nth 1 cur) "double") (when (member (nth 2 cur) knowntypes) (setcdr cur (cdr (assoc (nth 2 cur) vars)))) (push (nth 0 cur) knowntypes)) ;; generate the tag (push (semantic-tag-new-variable (car cur) (cond ;; structures ((string= (cadr cur) "struct") (semantic-tag-new-type ;; this is just a placeholder (concat (car cur) "_struct") "struct" (mapcar (lambda (x) (semantic-tag-new-variable x nil nil :typemodifiers '("public") )) (nthcdr 2 cur)) nil)) ;; classes ((string= (cadr cur) "class") ;; include tags from class constructor ;; (contains whole class, including methods) (car (semanticdb-file-stream (concat (nth 1 (assoc (nth 2 cur) semanticdb-matlab-user-class-cache)) "/@" (nth 2 cur) "/" (nth 2 cur) ".m")))) (t nil))) tags)) tags))) (define-mode-local-override semantic-format-tag-prototype matlab-mode (tag &optional parent color) "Return a prototype string describing tag. For MATLAB, we have to mark builtin functions, since we currently cannot derive an argument list for them." (let ((class (semantic-tag-class tag)) (name (semantic-format-tag-name tag parent color)) str) (if (eq class 'function) (let* ((args (semantic-tag-function-arguments tag)) (argstr (semantic--format-tag-arguments args #'identity color)) (builtin (semantic-tag-get-attribute tag :builtin)) (doc (semantic-tag-docstring tag))) (if builtin (if color (setq builtin (semantic--format-colorize-text " [builtin] " 'keyword) argstr (semantic--format-colorize-text " arguments unavailable" 'label)) (setq builtin " [builtin] " argstr " arguments unavailable")) (setq builtin "")) (concat name builtin "(" (if args " " "") argstr " )")) (semantic-format-tag-prototype-default tag parent color)))) (defun semantic-idle-summary-format-matlab-mode (tag &optional parent color) "Describe TAG and display corresponding MATLAB 'lookfor' doc-string. Optional PARENT and COLOR specify additional details for the tag. See `semantic-format-tag-prototype-matlab-mode' for details." (let* ((proto (semantic-format-tag-prototype-matlab-mode tag nil color)) (doc (semantic-tag-docstring tag))) (concat proto " (" doc ")"))) (defcustom-mode-local-semantic-dependency-system-include-path matlab-mode semantic-matlab-dependency-system-include-path (when (and semantic-matlab-root-directory (file-exists-p semantic-matlab-root-directory)) (let ((path nil)) (mapcar (lambda (cur) (let ((tmp (expand-file-name cur semantic-matlab-root-directory))) (when (file-exists-p tmp) (push tmp path)))) semantic-matlab-system-paths-include) path)) "The system include paths from MATLAB.") (defvar semantic-idle-summary-function) ;; quiet compiler warning (not sure where this is defined) (defvar-mode-local matlab-mode semantic-idle-summary-function 'semantic-idle-summary-format-matlab-mode "Function to use when displaying tag information during idle time.") (defvar semantic-matlab-display-docstring t "Flag if function documentation should be displayed after completion.") (define-mode-local-override semantic-ia-insert-tag matlab-mode (tag) "Insert TAG into the current buffer based on completion." (insert (semantic-tag-name tag)) (let ((name (semantic-tag-name tag)) (tt (semantic-tag-class tag)) (args (semantic-tag-function-arguments tag)) (doc (semantic-tag-docstring tag))) (when (and (eq tt 'function) args (not (looking-at "\\s-*("))) (insert "(")) ;; delete trailing whitespaces when completing class methods (when (looking-at "\\(\\s-+\\)(") (delete-char (length (match-string 1)))) (when semantic-matlab-display-docstring (if (fboundp 'fame-message-nolog) (fame-message-nolog (semantic-idle-summary-format-matlab-mode tag nil t)))))) (define-mode-local-override semantic-ctxt-current-symbol matlab-mode (&optional point) "Return the current symbol the cursor is on at point in a list. This will include a list of type/field names when applicable." (let* ((case-fold-search semantic-case-fold) sym) (with-syntax-table semantic-lex-syntax-table (save-excursion (when point (goto-char point)) ;; go to beginning of symbol (skip-syntax-backward "w_") (setq sym (if (looking-at "[a-zA-Z_0-9]+") (match-string-no-properties 0) nil)) (if sym (cond ;; method call: var = method(class,args) ((progn (and (looking-back "[^=><~]=\\s-*" nil) (looking-at "[a-zA-Z_0-9]*\\s-*(\\([a-zA-Z_0-9]+\\),?"))) (list (match-string-no-properties 1) sym)) ;; class properties: var = get(class,'attribute') ((looking-back "\\(get\\|set\\)(\\s-*\\([a-zA-Z_0-9]+\\),'" nil) (list (match-string-no-properties 2) sym)) ;; (nested) structures or new-style classes ((looking-back "[^A-Za-z_0-9.]\\([A-Za-z_0-9.]+\\)\\." nil) (list (match-string-no-properties 1) sym)) (t (list sym))) nil))))) (define-mode-local-override semantic-ctxt-current-symbol-and-bounds matlab-mode (&optional point) "Return the current symbol and bounds the cursor is on at POINT. Uses `semantic-ctxt-current-symbol' to calculate the symbol. Return (PREFIX ENDSYM BOUNDS)." (let ((sym (semantic-ctxt-current-symbol-matlab-mode point)) bounds endsym) (save-excursion (when point (goto-char point)) (when sym ;; go to beginning of symbol (skip-syntax-backward "w_") (setq endsym (progn (looking-at "[a-zA-Z_0-9]+") (match-string-no-properties 0))) (setq bounds (cons (match-beginning 0) (match-end 0))) (list sym endsym bounds))))) (defvar semantic-imenu-bucketize-type-members) ;; quiet compiler warning ;;;###autoload (defun semantic-default-matlab-setup () "Set up a buffer for parsing of MATLAB files." ;; This will use our parser. (semantic-install-function-overrides '((parse-region . semantic-matlab-parse-region) (parse-changes . semantic-matlab-parse-changes))) (setq semantic-parser-name "MATLAB" ;; Setup a dummy parser table to enable parsing! semantic--parse-table t imenu-create-index-function 'semantic-create-imenu-index ;; semantic-command-separation-character "." semantic-type-relation-separator-character '(".") semantic-symbol->name-assoc-list '((function . "Function") (type . "Class") ) semantic-imenu-expandable-tag-classes '(function type) semantic-imenu-bucketize-file nil semantic-imenu-bucketize-type-members nil senator-step-at-start-end-tag-classes '(function) semantic-stickyfunc-sticky-classes '(function type) ) ) (provide 'semantic-matlab) ;;; semantic-matlab.el ends here matlab-mode/mlgud.el0000664000175000017500000013721314611713120015413 0ustar sebastiensebastien;;; mlgud.el --- parts of gud.el for matlab-shell -*- lexical-binding:t -*- ;; This contains parts of gud.el prefixed with matlab and modified to support `matlab-shell'. gud ;; does not support multiple debuggers. For matlab-shell, we'd need to be able to debug MATLAB in ;; `matlab-shell', while in another buffer uses `gud-gdb' or `gdb' from gud.el to debug C++ code. ;; Emacs 24 gud.el info: ;;; gud.el --- Grand Unified Debugger mode for running GDB and other debuggers ;; Copyright (C) 1992-1996, 1998, 2000-2015 Free Software Foundation, ;; Inc. ;; Author: Eric S. Raymond ;; Maintainer: emacs-devel@gnu.org ;; Keywords: unix, tools ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; The ancestral gdb.el was by W. Schelter . ;; It was later rewritten by rms. Some ideas were due to Masanobu. Grand ;; Unification (sdb/dbx support) by Eric S. Raymond Barry ;; Warsaw hacked the mode to use comint.el. Shane Hartman ;; added support for xdb (HPUX debugger). Rick Sladkey ;; wrote the GDB command completion code. Dave Love ;; added the IRIX kluge, re-implemented the Mips-ish variant ;; and added a menu. Brian D. Carlstrom combined the IRIX ;; kluge with the gud-xdb-directories hack producing gud-dbx-directories. ;; Derek L. Davies added support for jdb (Java ;; debugger.) ;;; Code: (require 'comint) (defvar gdb-active-process) (defvar gdb-define-alist) (defvar gdb-macro-info) (defvar gdb-show-changed-values) (defvar gdb-source-window) (defvar gdb-var-list) (defvar hl-line-mode) (defvar hl-line-sticky-flag) ;; ====================================================================== ;; MLGUD commands must be visible in C buffers visited by MLGUD (defgroup mlgud nil "The \"Grand Unified Debugger\" interface. Supported debuggers include gdb, sdb, dbx, xdb, perldb, pdb (Python), and jdb." :group 'processes :group 'tools) (defvar mlgud-marker-filter nil) (put 'mlgud-marker-filter 'permanent-local t) (defvar mlgud-find-file nil) (put 'mlgud-find-file 'permanent-local t) (defun mlgud-marker-filter (&rest args) (apply mlgud-marker-filter args)) (defvar mlgud-minor-mode nil) (put 'mlgud-minor-mode 'permanent-local t) (defvar mlgud-comint-buffer nil) (defvar mlgud-keep-buffer nil) (defun mlgud-symbol (sym &optional soft minor-mode) "Return the symbol used for SYM in MINOR-MODE. MINOR-MODE defaults to `mlgud-minor-mode'. The symbol returned is `mlgud--'. If SOFT is non-nil, returns nil if the symbol doesn't already exist." (unless (or minor-mode mlgud-minor-mode) (error "mlGud internal error")) (funcall (if soft 'intern-soft 'intern) (format "mlgud-%s-%s" (or minor-mode mlgud-minor-mode) sym))) (defun mlgud-val (sym &optional minor-mode) "Return the value of `mlgud-symbol' SYM. Default to nil." (let ((sym (mlgud-symbol sym t minor-mode))) (if (boundp sym) (symbol-value sym)))) (defvar mlgud-running nil "Non-nil if debugged program is running. Used to gray out relevant toolbar icons.") (defvar mlgud-target-name "--unknown--" "The apparent name of the program being debugged in a mlgud buffer.") ;; Use existing Info buffer, if possible. (defun mlgud-goto-info () "Go to relevant Emacs info node." (interactive) (if (eq mlgud-minor-mode 'gdbmi) (info-other-window "(emacs)GDB Graphical Interface") (info-other-window "(emacs)Debuggers"))) (defun mlgud-tool-bar-item-visible-no-fringe () (not (or (eq (buffer-local-value 'major-mode (window-buffer)) 'speedbar-mode) (eq (buffer-local-value 'major-mode (window-buffer)) 'gdb-memory-mode) (and (eq mlgud-minor-mode 'gdbmi) (> (car (window-fringes)) 0))))) (declare-function gdb-gud-context-command "gdb-mi.el") (defun mlgud-stop-subjob () (interactive) (with-current-buffer mlgud-comint-buffer (cond ((string-equal mlgud-target-name "emacs") (comint-stop-subjob)) ((eq mlgud-minor-mode 'jdb) (mlgud-call "suspend")) ((eq mlgud-minor-mode 'gdbmi) (mlgud-call (gdb-gud-context-command "-exec-interrupt"))) (t (comint-interrupt-subjob))))) (defvar mlgud-tool-bar-map (let ((map (make-sparse-keymap))) (dolist (x '((mlgud-break . "gud/break") (mlgud-remove . "gud/remove") (mlgud-print . "gud/print") (mlgud-pstar . "gud/pstar") (mlgud-pp . "gud/pp") (mlgud-watch . "gud/watch") (mlgud-run . "gud/run") (mlgud-go . "gud/go") (mlgud-stop-subjob . "gud/stop") (mlgud-cont . "gud/cont") (mlgud-until . "gud/until") (mlgud-next . "gud/next") (mlgud-step . "gud/step") (mlgud-finish . "gud/finish") (mlgud-nexti . "gud/nexti") (mlgud-stepi . "gud/stepi") (mlgud-up . "gud/up") (mlgud-down . "gud/down") (mlgud-goto-info . "info")) map) (tool-bar-local-item-from-menu (car x) (cdr x) map)))) (defun mlgud-file-name (f) "Transform a relative file name to an absolute file name. Uses `mlgud--directories' to find the source files." ;; When `default-directory' is a remote file name, prepend its ;; remote part to f, which is the local file name. Fortunately, ;; `file-remote-p' returns exactly this remote file name part (or ;; nil otherwise). (setq f (concat (or (file-remote-p default-directory) "") f)) (if (file-exists-p f) (expand-file-name f) (let ((directories (mlgud-val 'directories)) (result nil)) (while directories (let ((path (expand-file-name f (car directories)))) (if (file-exists-p path) (setq result path directories nil))) (setq directories (cdr directories))) result))) (declare-function gdb-create-define-alist "gdb-mi" ()) (defun mlgud-find-file (file) ;; Don't get confused by double slashes in the name that comes from GDB. (while (string-match "//+" file) (setq file (replace-match "/" t t file))) (let ((minor-mode mlgud-minor-mode) (buf (funcall (or mlgud-find-file 'mlgud-file-name) file))) (when (stringp buf) (setq buf (and (file-readable-p buf) (find-file-noselect buf 'nowarn)))) (when buf ;; Copy `mlgud-minor-mode' to the found buffer to turn on the menu. (with-current-buffer buf (setq-local mlgud-minor-mode minor-mode) (if (boundp 'tool-bar-map) ; not --without-x (setq-local tool-bar-map mlgud-tool-bar-map)) (when (and mlgud-tooltip-mode (eq mlgud-minor-mode 'gdbmi)) (make-local-variable 'gdb-define-alist) (unless gdb-define-alist (gdb-create-define-alist)) (add-hook 'after-save-hook 'gdb-create-define-alist nil t)) (make-local-variable 'mlgud-keep-buffer)) buf))) ;; ====================================================================== ;; command definition ;; This macro is used below to define some basic debugger interface commands. ;; Of course you may use `mlgud-def' with any other debugger command, including ;; user defined ones. ;; A macro call like (mlgud-def FUNC CMD KEY DOC) expands to a form ;; which defines FUNC to send the command CMD to the debugger, gives ;; it the docstring DOC, and binds that function to KEY in the MLGUD ;; major mode. The function is also bound in the global keymap with the ;; MLGUD prefix. (defmacro mlgud-def (func cmd &optional doc) "Define FUNC to be a command sending CMD, with optional doc string DOC. Certain %-escapes in the string arguments are interpreted specially if present. These are: %f -- Name (without directory) of current source file. %F -- Name (without directory or extension) of current source file. %d -- Directory of current source file. %l -- Number of current source line. %e -- Text of the C lvalue or function-call expression surrounding point. %a -- Text of the hexadecimal address surrounding point. %p -- Prefix argument to the command (if any) as a number. %c -- Fully qualified class name derived from the expression surrounding point (jdb only). The `current' source file is the file of the current buffer (if we're in a C file) or the source file current at the last break or step (if we're in the MLGUD buffer). The `current' line is that of the current buffer (if we're in a source file) or the source line number at the last break or step (if we're in the MLGUD buffer)." `(progn (defalias ',func (lambda (arg) ,@(if doc (list doc)) (interactive "p") (if (not mlgud-running) ,(if (stringp cmd) `(mlgud-call ,cmd arg) cmd)))) )) ;; Where mlgud-display-frame should put the debugging arrow; a cons of ;; (filename . line-number). This is set by the marker-filter, which scans ;; the debugger's output for indications of the current program counter. (defvar mlgud-last-frame nil) ;; Used by mlgud-refresh, which should cause mlgud-display-frame to redisplay ;; the last frame, even if it's been called before and mlgud-last-frame has ;; been set to nil. (defvar mlgud-last-last-frame nil) ;; All debugger-specific information is collected here. ;; Here's how it works, in case you ever need to add a debugger to the mode. ;; ;; Each entry must define the following at startup: ;; ;; ;; comint-prompt-regexp ;; mlgud--massage-args ;; mlgud--marker-filter ;; mlgud--find-file ;; ;; The job of the massage-args method is to modify the given list of ;; debugger arguments before running the debugger. ;; ;; The job of the marker-filter method is to detect file/line markers in ;; strings and set the global mlgud-last-frame to indicate what display ;; action (if any) should be triggered by the marker. Note that only ;; whatever the method *returns* is displayed in the buffer; thus, you ;; can filter the debugger's output, interpreting some and passing on ;; the rest. ;; ;; The job of the find-file method is to visit and return the buffer indicated ;; by the car of mlgud-tag-frame. This may be a file name, a tag name, or ;; something else. ;; ====================================================================== ;; speedbar support functions and variables. (eval-when-compile (require 'dframe)) ; for dframe-with-attached-buffer (defvar mlgud-last-speedbar-stackframe nil "Description of the currently displayed MLGUD stack. The value t means that there is no stack, and we are in display-file mode.") (defvar mlgud-speedbar-key-map nil "Keymap used when in the buffers display mode.") ;; At runtime, will be pulled in as a require of speedbar. (declare-function dframe-message "dframe" (fmt &rest args)) (defun mlgud-speedbar-item-info () "Display the data type of the watch expression element." (let ((var (nth (- (line-number-at-pos (point)) 2) gdb-var-list))) (if (nth 7 var) (dframe-message "%s: %s" (nth 7 var) (nth 3 var)) (dframe-message "%s" (nth 3 var))))) (declare-function speedbar-make-specialized-keymap "speedbar" ()) (declare-function speedbar-add-expansion-list "speedbar" (new-list)) (defvar speedbar-mode-functions-list) (defun mlgud-install-speedbar-variables () "Install those variables used by speedbar to enhance mlgud/gdb." (unless mlgud-speedbar-key-map (setq mlgud-speedbar-key-map (speedbar-make-specialized-keymap)) (define-key mlgud-speedbar-key-map "j" 'speedbar-edit-line) (define-key mlgud-speedbar-key-map "e" 'speedbar-edit-line) (define-key mlgud-speedbar-key-map "\C-m" 'speedbar-edit-line) (define-key mlgud-speedbar-key-map " " 'speedbar-toggle-line-expansion) (define-key mlgud-speedbar-key-map "D" 'gdb-var-delete) (define-key mlgud-speedbar-key-map "p" 'mlgud-pp)) (speedbar-add-expansion-list '("mlMLGUD" mlgud-speedbar-menu-items mlgud-speedbar-key-map mlgud-expansion-speedbar-buttons)) (add-to-list 'speedbar-mode-functions-list '("mlMLGUD" (speedbar-item-info . mlgud-speedbar-item-info) (speedbar-line-directory . ignore)))) (defvar mlgud-speedbar-menu-items '(["Jump to stack frame" speedbar-edit-line :visible (not (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi))] ["Edit value" speedbar-edit-line :visible (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi)] ["Delete expression" gdb-var-delete :visible (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi)] ["Auto raise frame" gdb-speedbar-auto-raise :style toggle :selected gdb-speedbar-auto-raise :visible (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi)] ("Output Format" :visible (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi) ["Binary" (gdb-var-set-format "binary") t] ["Natural" (gdb-var-set-format "natural") t] ["Hexadecimal" (gdb-var-set-format "hexadecimal") t])) "Additional menu items to add to the speedbar frame.") ;; Make sure our special speedbar mode is loaded (if (featurep 'speedbar) (mlgud-install-speedbar-variables) (add-hook 'speedbar-load-hook 'mlgud-install-speedbar-variables)) (defun mlgud-expansion-speedbar-buttons (_directory _zero) "Wrapper for call to `speedbar-add-expansion-list'. DIRECTORY and ZERO are not used, but are required by the caller." (mlgud-speedbar-buttons mlgud-comint-buffer)) (declare-function speedbar-make-tag-line "speedbar" (type char func data tag tfunc tdata tface depth)) (declare-function speedbar-remove-localized-speedbar-support "speedbar" (buffer)) (declare-function speedbar-insert-button "speedbar" (text face mouse function &optional token prevline)) (defun mlgud-speedbar-buttons (buffer) "Create a speedbar display based on the current state of MLGUD. If the MLGUD BUFFER is not running a supported debugger, then turn off the specialized speedbar mode. BUFFER is not used, but is required by the caller." (when (and mlgud-comint-buffer ;; mlgud-comint-buffer might be killed (buffer-name mlgud-comint-buffer)) (let* ((minor-mode (with-current-buffer buffer mlgud-minor-mode)) (window (get-buffer-window (current-buffer) 0)) (start (window-start window)) (p (window-point window))) (cond ((eq minor-mode 'gdbmi) (erase-buffer) (insert "Watch Expressions:\n") (let ((var-list gdb-var-list) parent) (while var-list (let* (char (depth 0) (start 0) (var (car var-list)) (varnum (car var)) (expr (nth 1 var)) (type (if (nth 3 var) (nth 3 var) " ")) (value (nth 4 var)) (status (nth 5 var)) (has-more (nth 6 var))) (put-text-property 0 (length expr) 'face font-lock-variable-name-face expr) (put-text-property 0 (length type) 'face font-lock-type-face type) (while (string-match "\\." varnum start) (setq depth (1+ depth) start (1+ (match-beginning 0)))) (if (eq depth 0) (setq parent nil)) (if (and (or (not has-more) (string-equal has-more "0")) (or (equal (nth 2 var) "0") (and (equal (nth 2 var) "1") (string-match "char \\*$" type)) )) (speedbar-make-tag-line 'bracket ?? nil nil (concat expr "\t" value) (if (or parent (eq status 'out-of-scope)) nil 'gdb-edit-value) nil (if gdb-show-changed-values (or parent (pcase status (`changed 'font-lock-warning-face) (`out-of-scope 'shadow) (_ t))) t) depth) (if (eq status 'out-of-scope) (setq parent 'shadow)) (if (and (nth 1 var-list) (string-match (concat varnum "\\.") (car (nth 1 var-list)))) (setq char ?-) (setq char ?+)) (if (string-match "\\*$\\|\\*&$" type) (speedbar-make-tag-line 'bracket char 'gdb-speedbar-expand-node varnum (concat expr "\t" type "\t" value) (if (or parent (eq status 'out-of-scope)) nil 'gdb-edit-value) nil (if gdb-show-changed-values (or parent (pcase status (`changed 'font-lock-warning-face) (`out-of-scope 'shadow) (_ t))) t) depth) (speedbar-make-tag-line 'bracket char 'gdb-speedbar-expand-node varnum (concat expr "\t" type) nil nil (if (and (or parent status) gdb-show-changed-values) 'shadow t) depth)))) (setq var-list (cdr var-list))))) (t (unless (and (save-excursion (goto-char (point-min)) (looking-at "Current Stack:")) (equal mlgud-last-last-frame mlgud-last-speedbar-stackframe)) (let ((mlgud-frame-list (cond ;; Add more debuggers here! (t (speedbar-remove-localized-speedbar-support buffer) nil)))) (erase-buffer) (if (not mlgud-frame-list) (insert "No Stack frames\n") (insert "Current Stack:\n")) (dolist (frame mlgud-frame-list) (insert (nth 1 frame) ":\n") (if (= (length frame) 2) (progn (speedbar-insert-button (car frame) 'speedbar-directory-face nil nil nil t)) (speedbar-insert-button (car frame) 'speedbar-file-face 'speedbar-highlight-face (cond ((memq minor-mode '(gdbmi gdb)) 'mlgud-gdb-goto-stackframe) (t (error "Should never be here"))) frame t)))) (setq mlgud-last-speedbar-stackframe mlgud-last-last-frame)))) (set-window-start window start) (set-window-point window p)))) ;; When we send a command to the debugger via mlgud-call, it's annoying ;; to see the command and the new prompt inserted into the debugger's ;; buffer; we have other ways of knowing the command has completed. ;; ;; If the buffer looks like this: ;; -------------------- ;; (gdb) set args foo bar ;; (gdb) -!- ;; -------------------- ;; (the -!- marks the location of point), and we type `C-x SPC' in a ;; source file to set a breakpoint, we want the buffer to end up like ;; this: ;; -------------------- ;; (gdb) set args foo bar ;; Breakpoint 1 at 0x92: file make-docfile.c, line 49. ;; (gdb) -!- ;; -------------------- ;; Essentially, the old prompt is deleted, and the command's output ;; and the new prompt take its place. ;; ;; Not echoing the command is easy enough; you send it directly using ;; process-send-string, and it never enters the buffer. However, ;; getting rid of the old prompt is trickier; you don't want to do it ;; when you send the command, since that will result in an annoying ;; flicker as the prompt is deleted, redisplay occurs while Emacs ;; waits for a response from the debugger, and the new prompt is ;; inserted. Instead, we'll wait until we actually get some output ;; from the subprocess before we delete the prompt. If the command ;; produced no output other than a new prompt, that prompt will most ;; likely be in the first chunk of output received, so we will delete ;; the prompt and then replace it with an identical one. If the ;; command produces output, the prompt is moving anyway, so the ;; flicker won't be annoying. ;; ;; So - when we want to delete the prompt upon receipt of the next ;; chunk of debugger output, we position mlgud-delete-prompt-marker at ;; the start of the prompt; the process filter will notice this, and ;; delete all text between it and the process output marker. If ;; mlgud-delete-prompt-marker points nowhere, we leave the current ;; prompt alone. (defvar mlgud-delete-prompt-marker nil) (put 'mlgud-mode 'mode-class 'special) (define-derived-mode mlgud-mode comint-mode "Debugger" "Major mode for interacting with an inferior debugger process. You start it up with one of the commands M-x gdb, M-x sdb, M-x dbx, M-x perldb, M-x xdb, or M-x jdb. Each entry point finishes by executing a hook; `gdb-mode-hook', `sdb-mode-hook', `dbx-mode-hook', `perldb-mode-hook', `xdb-mode-hook', or `jdb-mode-hook' respectively. After startup, the following commands are available in both the MLGUD interaction buffer and any source buffer MLGUD visits due to a breakpoint stop or step operation: \\[mlgud-break] sets a breakpoint at the current file and line. In the MLGUD buffer, the current file and line are those of the last breakpoint or step. In a source buffer, they are the buffer's file and current line. \\[mlgud-remove] removes breakpoints on the current file and line. \\[mlgud-refresh] displays in the source window the last line referred to in the mlgud buffer. \\[mlgud-step], \\[mlgud-next], and \\[mlgud-stepi] do a step-one-line, step-one-line (not entering function calls), and step-one-instruction and then update the source window with the current file and position. \\[mlgud-cont] continues execution. \\[mlgud-print] tries to find the largest C lvalue or function-call expression around point, and sends it to the debugger for value display. The above commands are common to all supported debuggers except xdb which does not support stepping instructions. Under gdb, sdb and xdb, \\[mlgud-tbreak] behaves exactly like \\[mlgud-break], except that the breakpoint is temporary; that is, it is removed when execution stops on it. Under gdb, dbx, and xdb, \\[mlgud-up] pops up through an enclosing stack frame. \\[mlgud-down] drops back down through one. If you are using gdb or xdb, \\[mlgud-finish] runs execution to the return from the current function and stops. All the keystrokes above are accessible in the MLGUD buffer with the prefix C-c, and in all buffers through the prefix C-x C-a. All pre-defined functions for which the concept make sense repeat themselves the appropriate number of times if you give a prefix argument. You may use the `mlgud-def' macro in the initialization hook to define other commands. Other commands for interacting with the debugger process are inherited from comint mode, which see." (setq mode-line-process '(":%s")) (define-key (current-local-map) "\C-c\C-l" 'mlgud-refresh) (set (make-local-variable 'mlgud-last-frame) nil) (if (boundp 'tool-bar-map) ; not --without-x (setq-local tool-bar-map mlgud-tool-bar-map)) (make-local-variable 'comint-prompt-regexp) ;; Don't put repeated commands in command history many times. (set (make-local-variable 'comint-input-ignoredups) t) (make-local-variable 'paragraph-start) (set (make-local-variable 'mlgud-delete-prompt-marker) (make-marker)) (add-hook 'kill-buffer-hook 'mlgud-kill-buffer-hook nil t)) (defun mlgud-set-buffer () (when (derived-mode-p 'mlgud-mode) (setq mlgud-comint-buffer (current-buffer)))) (defvar mlgud-filter-defer-flag nil "Non-nil means don't process anything from the debugger right now. It is saved for when this flag is not set.") ;; These functions are responsible for inserting output from your debugger ;; into the buffer. The hard work is done by the method that is ;; the value of mlgud-marker-filter. (defvar mlgud-filter-pending-text nil "Non-nil means this is text that has been saved for later in `mlgud-filter'.") (defun mlgud-filter (proc string) ;; Here's where the actual buffer insertion is done (let (output process-window) (if (buffer-name (process-buffer proc)) (if mlgud-filter-defer-flag ;; If we can't process any text now, ;; save it for later. (setq mlgud-filter-pending-text (concat (or mlgud-filter-pending-text "") string)) ;; If we have to ask a question during the processing, ;; defer any additional text that comes from the debugger ;; during that time. (let ((mlgud-filter-defer-flag t)) ;; Process now any text we previously saved up. (if mlgud-filter-pending-text (setq string (concat mlgud-filter-pending-text string) mlgud-filter-pending-text nil)) (with-current-buffer (process-buffer proc) ;; If we have been so requested, delete the debugger prompt. (save-restriction (widen) (if (marker-buffer mlgud-delete-prompt-marker) (let ((inhibit-read-only t)) (delete-region (process-mark proc) mlgud-delete-prompt-marker) (comint-update-fence) (set-marker mlgud-delete-prompt-marker nil))) ;; Save the process output, checking for source file markers. (setq output (mlgud-marker-filter string)) ;; Check for a filename-and-line number. ;; Don't display the specified file ;; unless (1) point is at or after the position where output appears ;; and (2) this buffer is on the screen. (setq process-window (and mlgud-last-frame (>= (point) (process-mark proc)) (get-buffer-window (current-buffer))))) ;; Let the comint filter do the actual insertion. ;; That lets us inherit various comint features. (comint-output-filter proc output)) ;; Put the arrow on the source line. ;; This must be outside of the save-excursion ;; in case the source file is our current buffer. (if process-window (with-selected-window process-window (mlgud-display-frame)) ;; We have to be in the proper buffer, (process-buffer proc), ;; but not in a save-excursion, because that would restore point. (with-current-buffer (process-buffer proc) (mlgud-display-frame)))) ;; If we deferred text that arrived during this processing, ;; handle it now. (if mlgud-filter-pending-text (mlgud-filter proc "")))))) (defvar mlgud-minor-mode-type nil) (defvar mlgud-overlay-arrow-position nil) (add-to-list 'overlay-arrow-variable-list 'mlgud-overlay-arrow-position) (declare-function gdb-reset "gdb-mi" ()) (declare-function speedbar-change-initial-expansion-list "speedbar" (new)) (defvar speedbar-previously-used-expansion-list-name) (defun mlgud-sentinel (proc msg) (cond ((null (buffer-name (process-buffer proc))) ;; buffer killed ;; Stop displaying an arrow in a source file. (setq mlgud-overlay-arrow-position nil) (set-process-buffer proc nil) (if (and (boundp 'speedbar-initial-expansion-list-name) (string-equal speedbar-initial-expansion-list-name "mlMLGUD")) (speedbar-change-initial-expansion-list speedbar-previously-used-expansion-list-name)) (if (eq mlgud-minor-mode-type 'gdbmi) (gdb-reset) (mlgud-reset))) ((memq (process-status proc) '(signal exit)) ;; Stop displaying an arrow in a source file. (setq mlgud-overlay-arrow-position nil) (if (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi) (gdb-reset) (mlgud-reset)) (let* ((obuf (current-buffer))) ;; save-excursion isn't the right thing if ;; process-buffer is current-buffer (unwind-protect (progn ;; Write something in the MLGUD buffer and hack its mode line, (set-buffer (process-buffer proc)) ;; Fix the mode line. (setq mode-line-process (concat ":" (symbol-name (process-status proc)))) (force-mode-line-update) (if (eobp) (insert ?\n mode-name " " msg) (save-excursion (goto-char (point-max)) (insert ?\n mode-name " " msg))) ;; If buffer and mode line will show that the process ;; is dead, we can delete it now. Otherwise it ;; will stay around until M-x list-processes. (delete-process proc)) ;; Restore old buffer, but don't restore old point ;; if obuf is the mlgud buffer. (set-buffer obuf)))))) (defun mlgud-kill-buffer-hook () (setq mlgud-minor-mode-type mlgud-minor-mode) (condition-case nil (progn (kill-process (get-buffer-process (current-buffer))) (delete-process (get-process "gdb-inferior"))) (error nil))) (defun mlgud-reset () (dolist (buffer (buffer-list)) (unless (eq buffer mlgud-comint-buffer) (with-current-buffer buffer (when mlgud-minor-mode (setq mlgud-minor-mode nil) (kill-local-variable 'tool-bar-map)))))) (defun mlgud-display-frame () "Find and obey the last filename-and-line marker from the debugger. Obeying it means displaying in another window the specified file and line." (interactive) (when mlgud-last-frame (mlgud-set-buffer) (mlgud-display-line (car mlgud-last-frame) (cdr mlgud-last-frame)) (setq mlgud-last-last-frame mlgud-last-frame mlgud-last-frame nil))) (declare-function global-hl-line-highlight "hl-line" ()) (declare-function hl-line-highlight "hl-line" ()) (declare-function gdb-display-source-buffer "gdb-mi" (buffer)) ;; Make sure the file named TRUE-FILE is in a buffer that appears on the screen ;; and that its line LINE is visible. ;; Put the overlay-arrow on the line LINE in that buffer. ;; Most of the trickiness in here comes from wanting to preserve the current ;; region-restriction if that's possible. We use an explicit display-buffer ;; to get around the fact that this is called inside a save-excursion. (defun mlgud-display-line (true-file line) (let* ((last-nonmenu-event t) ; Prevent use of dialog box for questions. (buffer (with-current-buffer mlgud-comint-buffer (mlgud-find-file true-file))) (window (and buffer (or (get-buffer-window buffer) (display-buffer buffer)))) (pos)) (when buffer (with-current-buffer buffer (unless (or (verify-visited-file-modtime buffer) mlgud-keep-buffer) (if (yes-or-no-p (format "File %s changed on disk. Reread from disk? " (buffer-name))) (revert-buffer t t) (setq mlgud-keep-buffer t))) (save-restriction (widen) (goto-char (point-min)) (forward-line (1- line)) (setq pos (point)) (or mlgud-overlay-arrow-position (setq mlgud-overlay-arrow-position (make-marker))) (set-marker mlgud-overlay-arrow-position (point) (current-buffer)) ;; If they turned on hl-line, move the hl-line highlight to ;; the arrow's line. (when (featurep 'hl-line) (cond (global-hl-line-mode (global-hl-line-highlight)) ((and hl-line-mode hl-line-sticky-flag) (hl-line-highlight))))) (cond ((or (< pos (point-min)) (> pos (point-max))) (widen) (goto-char pos)))) (when window (set-window-point window mlgud-overlay-arrow-position) (if (eq mlgud-minor-mode 'gdbmi) (setq gdb-source-window window)))))) ;; The mlgud-call function must do the right thing whether its invoking ;; keystroke is from the MLGUD buffer itself (via major-mode binding) ;; or a C buffer. In the former case, we want to supply data from ;; mlgud-last-frame. Here's how we do it: (defun mlgud-format-command (str arg) (let ((insource (not (eq (current-buffer) mlgud-comint-buffer))) (frame (or mlgud-last-frame mlgud-last-last-frame)) result) (while (and str (let ((case-fold-search nil)) (string-match "\\([^%]*\\)%\\([adefFlpc]\\)" str))) (let ((key (string-to-char (match-string 2 str))) subst) (cond ((eq key ?f) (setq subst (file-name-nondirectory (if insource (buffer-file-name) (car frame))))) ((eq key ?F) (setq subst (file-name-base (if insource (buffer-file-name) (car frame))))) ((eq key ?d) (setq subst (file-name-directory (if insource (buffer-file-name) (car frame))))) ((eq key ?l) (setq subst (int-to-string (if insource (save-restriction (widen) (+ (count-lines (point-min) (point)) (if (bolp) 1 0))) (cdr frame))))) ((eq key ?e) (setq subst (mlgud-find-expr))) ((eq key ?a) (setq subst (mlgud-read-address))) ((eq key ?p) (setq subst (if arg (int-to-string arg))))) (setq result (concat result (match-string 1 str) subst))) (setq str (substring str (match-end 2)))) ;; There might be text left in STR when the loop ends. (concat result str))) (defun mlgud-read-address () "Return a string containing the core-address found in the buffer at point." (save-match-data (save-excursion (let ((pt (point)) found begin) (setq found (if (search-backward "0x" (- pt 7) t) (point))) (cond (found (forward-char 2) (buffer-substring found (progn (re-search-forward "[^0-9a-f]") (forward-char -1) (point)))) (t (setq begin (progn (re-search-backward "[^0-9]") (forward-char 1) (point))) (forward-char 1) (re-search-forward "[^0-9]") (forward-char -1) (buffer-substring begin (point)))))))) (defun mlgud-call (fmt &optional arg) (let ((msg (mlgud-format-command fmt arg))) (message "Command: %s" msg) (sit-for 0) (mlgud-basic-call msg))) (defun mlgud-basic-call (command) "Invoke the debugger COMMAND displaying source in other window." (interactive) (mlgud-set-buffer) (let ((proc (get-buffer-process mlgud-comint-buffer))) (or proc (error "Current buffer has no process")) ;; Arrange for the current prompt to get deleted. (with-current-buffer mlgud-comint-buffer (save-excursion (save-restriction (widen) (if (marker-position mlgud-delete-prompt-marker) ;; We get here when printing an expression. (goto-char mlgud-delete-prompt-marker) (goto-char (process-mark proc)) (forward-line 0)) (if (looking-at comint-prompt-regexp) (set-marker mlgud-delete-prompt-marker (point))) (if (eq mlgud-minor-mode 'gdbmi) (apply comint-input-sender (list proc command)) (process-send-string proc (concat command "\n")))))))) (defun mlgud-refresh (&optional arg) "Fix up a possibly garbled display, and redraw the arrow." (interactive "P") (or mlgud-last-frame (setq mlgud-last-frame mlgud-last-last-frame)) (mlgud-display-frame) (recenter arg)) ;; Code for parsing expressions out of C or Fortran code. The single entry ;; point is mlgud-find-expr, which tries to return an lvalue expression from ;; around point. (defvar mlgud-find-expr-function 'mlgud-find-c-expr) (defun mlgud-find-expr (&rest args) (let ((expr (if (and transient-mark-mode mark-active) (buffer-substring (region-beginning) (region-end)) (apply mlgud-find-expr-function args)))) (save-match-data (if (string-match "\n" expr) (error "Expression must not include a newline")) (with-current-buffer mlgud-comint-buffer (save-excursion (goto-char (process-mark (get-buffer-process mlgud-comint-buffer))) (forward-line 0) (when (looking-at comint-prompt-regexp) (set-marker mlgud-delete-prompt-marker (point)) (set-marker-insertion-type mlgud-delete-prompt-marker t)) (unless (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'jdb) (insert (concat expr " = ")))))) expr)) ;; The next eight functions are hacked from gdbsrc.el by ;; Debby Ayers , ;; Rich Schaefer Schlumberger, Austin, Tx. (defun mlgud-find-c-expr () "Returns the expr that surrounds point." (interactive) (save-excursion (let ((p (point)) (expr (mlgud-innermost-expr)) (test-expr (mlgud-prev-expr))) (while (and test-expr (mlgud-expr-compound test-expr expr)) (let ((prev-expr expr)) (setq expr (cons (car test-expr) (cdr expr))) (goto-char (car expr)) (setq test-expr (mlgud-prev-expr)) ;; If we just pasted on the condition of an if or while, ;; throw it away again. (if (member (buffer-substring (car test-expr) (cdr test-expr)) '("if" "while" "for")) (setq test-expr nil expr prev-expr)))) (goto-char p) (setq test-expr (mlgud-next-expr)) (while (mlgud-expr-compound expr test-expr) (setq expr (cons (car expr) (cdr test-expr))) (setq test-expr (mlgud-next-expr))) (buffer-substring (car expr) (cdr expr))))) (defun mlgud-innermost-expr () "Returns the smallest expr that point is in; move point to beginning of it. The expr is represented as a cons cell, where the car specifies the point in the current buffer that marks the beginning of the expr and the cdr specifies the character after the end of the expr." (let ((p (point)) begin end) (mlgud-backward-sexp) (setq begin (point)) (mlgud-forward-sexp) (setq end (point)) (if (>= p end) (progn (setq begin p) (goto-char p) (mlgud-forward-sexp) (setq end (point))) ) (goto-char begin) (cons begin end))) (defun mlgud-backward-sexp () "Version of `backward-sexp' that catches errors." (condition-case nil (backward-sexp) (error t))) (defun mlgud-forward-sexp () "Version of `forward-sexp' that catches errors." (condition-case nil (forward-sexp) (error t))) (defun mlgud-prev-expr () "Returns the previous expr, point is set to beginning of that expr. The expr is represented as a cons cell, where the car specifies the point in the current buffer that marks the beginning of the expr and the cdr specifies the character after the end of the expr" (let ((begin) (end)) (mlgud-backward-sexp) (setq begin (point)) (mlgud-forward-sexp) (setq end (point)) (goto-char begin) (cons begin end))) (defun mlgud-next-expr () "Returns the following expr, point is set to beginning of that expr. The expr is represented as a cons cell, where the car specifies the point in the current buffer that marks the beginning of the expr and the cdr specifies the character after the end of the expr." (let ((begin) (end)) (mlgud-forward-sexp) (mlgud-forward-sexp) (setq end (point)) (mlgud-backward-sexp) (setq begin (point)) (cons begin end))) (defun mlgud-expr-compound-sep (span-start span-end) "Scan from SPAN-START to SPAN-END for punctuation characters. If `->' is found, return `?.'. If `.' is found, return `?.'. If any other punctuation is found, return `??'. If no punctuation is found, return `? '." (let ((result ?\s) (syntax)) (while (< span-start span-end) (setq syntax (char-syntax (char-after span-start))) (cond ((= syntax ?\s) t) ((= syntax ?.) (setq syntax (char-after span-start)) (cond ((= syntax ?.) (setq result ?.)) ((and (= syntax ?-) (= (char-after (+ span-start 1)) ?>)) (setq result ?.) (setq span-start (+ span-start 1))) (t (setq span-start span-end) (setq result ??))))) (setq span-start (+ span-start 1))) result)) (defun mlgud-expr-compound (first second) "Non-nil if concatenating FIRST and SECOND makes a single C expression. The two exprs are represented as a cons cells, where the car specifies the point in the current buffer that marks the beginning of the expr and the cdr specifies the character after the end of the expr. Link exprs of the form: Expr -> Expr Expr . Expr Expr (Expr) Expr [Expr] (Expr) Expr [Expr] Expr" (let ((span-start (cdr first)) (span-end (car second)) (syntax)) (setq syntax (mlgud-expr-compound-sep span-start span-end)) (cond ((= (car first) (car second)) nil) ((= (cdr first) (cdr second)) nil) ((= syntax ?.) t) ((= syntax ?\s) (setq span-start (char-after (- span-start 1))) (setq span-end (char-after span-end)) (cond ((= span-start ?\)) t) ((= span-start ?\]) t) ((= span-end ?\() t) ((= span-end ?\[) t) (t nil))) (t nil)))) ;;; tooltips for MLGUD ;;; Customizable settings (defvar tooltip-mode) ;;;###autoload (define-minor-mode mlgud-tooltip-mode "Toggle the display of MLGUD tooltips. With a prefix argument ARG, enable the feature if ARG is positive, and disable it otherwise. If called from Lisp, enable it if ARG is omitted or nil." :global t :group 'mlgud :group 'tooltip (require 'tooltip) (if mlgud-tooltip-mode (progn (add-hook 'change-major-mode-hook 'mlgud-tooltip-change-major-mode) (add-hook 'pre-command-hook 'tooltip-hide) (add-hook 'tooltip-functions 'mlgud-tooltip-tips) (define-key global-map [mouse-movement] 'mlgud-tooltip-mouse-motion)) (unless tooltip-mode (remove-hook 'pre-command-hook 'tooltip-hide) (remove-hook 'change-major-mode-hook 'mlgud-tooltip-change-major-mode) (remove-hook 'tooltip-functions 'mlgud-tooltip-tips) (define-key global-map [mouse-movement] 'ignore))) (mlgud-tooltip-activate-mouse-motions-if-enabled) (if (and mlgud-comint-buffer (buffer-name mlgud-comint-buffer); mlgud-comint-buffer might be killed (eq (buffer-local-value 'mlgud-minor-mode mlgud-comint-buffer) 'gdbmi)) (if mlgud-tooltip-mode (progn (dolist (buffer (buffer-list)) (unless (eq buffer mlgud-comint-buffer) (with-current-buffer buffer (when (and (eq mlgud-minor-mode 'gdbmi) (not (string-match "\\`\\*.+\\*\\'" (buffer-name)))) (make-local-variable 'gdb-define-alist) (gdb-create-define-alist) (add-hook 'after-save-hook 'gdb-create-define-alist nil t)))))) (kill-local-variable 'gdb-define-alist) (remove-hook 'after-save-hook 'gdb-create-define-alist t)))) (defcustom mlgud-tooltip-modes '(mlgud-mode c-mode c++-mode fortran-mode python-mode) "List of modes for which to enable MLGUD tooltips." :type 'sexp :group 'mlgud :group 'tooltip) (defcustom mlgud-tooltip-display '((eq (tooltip-event-buffer mlgud-tooltip-event) (marker-buffer mlgud-overlay-arrow-position))) "List of forms determining where MLGUD tooltips are displayed. Forms in the list are combined with AND. The default is to display only tooltips in the buffer containing the overlay arrow." :type 'sexp :group 'mlgud :group 'tooltip) (defcustom mlgud-tooltip-echo-area nil "Use the echo area instead of frames for MLGUD tooltips." :type 'boolean :group 'mlgud :group 'tooltip) (make-obsolete-variable 'mlgud-tooltip-echo-area "disable Tooltip mode instead" "24.4" 'set) ;;; Reacting on mouse movements (defun mlgud-tooltip-change-major-mode () "Function added to `change-major-mode-hook' when tooltip mode is on." (add-hook 'post-command-hook 'mlgud-tooltip-activate-mouse-motions-if-enabled)) (defun mlgud-tooltip-activate-mouse-motions-if-enabled () "Reconsider for all buffers whether mouse motion events are desired." (remove-hook 'post-command-hook 'mlgud-tooltip-activate-mouse-motions-if-enabled) (dolist (buffer (buffer-list)) (with-current-buffer buffer (if (and mlgud-tooltip-mode (memq major-mode mlgud-tooltip-modes)) (mlgud-tooltip-activate-mouse-motions t) (mlgud-tooltip-activate-mouse-motions nil))))) (defvar mlgud-tooltip-mouse-motions-active nil "Locally t in a buffer if tooltip processing of mouse motion is enabled.") ;; We don't set track-mouse globally because this is a big redisplay ;; problem in buffers having a pre-command-hook or such installed, ;; which does a set-buffer, like the summary buffer of Gnus. Calling ;; set-buffer prevents redisplay optimizations, so every mouse motion ;; would be accompanied by a full redisplay. (defun mlgud-tooltip-activate-mouse-motions (activatep) "Activate/deactivate mouse motion events for the current buffer. ACTIVATEP non-nil means activate mouse motion events." (if activatep (progn (set (make-local-variable 'mlgud-tooltip-mouse-motions-active) t) (set (make-local-variable 'track-mouse) t)) (when mlgud-tooltip-mouse-motions-active (kill-local-variable 'mlgud-tooltip-mouse-motions-active) (kill-local-variable 'track-mouse)))) (defvar tooltip-last-mouse-motion-event) (declare-function tooltip-hide "tooltip" (&optional ignored-arg)) (declare-function tooltip-start-delayed-tip "tooltip" ()) (defun mlgud-tooltip-mouse-motion (event) "Command handler for mouse movement events in `global-map'." (interactive "e") (tooltip-hide) (when (car (mouse-pixel-position)) (setq tooltip-last-mouse-motion-event (copy-sequence event)) (tooltip-start-delayed-tip))) ;;; Tips for `mlgud' (defvar mlgud-tooltip-dereference nil "Non-nil means print expressions with a `*' in front of them. For C this would dereference a pointer expression.") (defvar mlgud-tooltip-event nil "The mouse movement event that led to a tooltip display. This event can be examined by forms in `mlgud-tooltip-display'.") (defun mlgud-tooltip-dereference (&optional arg) "Toggle whether tooltips should show `* expr' or `expr'. With arg, dereference expr if ARG is positive, otherwise do not dereference." (interactive "P") (setq mlgud-tooltip-dereference (if (null arg) (not mlgud-tooltip-dereference) (> (prefix-numeric-value arg) 0))) (message "Dereferencing is now %s." (if mlgud-tooltip-dereference "on" "off"))) (defvar tooltip-use-echo-area) (declare-function tooltip-show "tooltip" (text &optional use-echo-area)) (declare-function tooltip-strip-prompt "tooltip" (process output)) ; This will only display data that comes in one chunk. ; Larger arrays (say 400 elements) are displayed in ; the tooltip incompletely and spill over into the mlgud buffer. ; Switching the process-filter creates timing problems and ; it may be difficult to do better. Using GDB/MI as in ; gdb-mi.el gets around this problem. (defun mlgud-tooltip-process-output (process output) "Process debugger output and show it in a tooltip window." (remove-function (process-filter process) #'mlgud-tooltip-process-output) (tooltip-show (tooltip-strip-prompt process output) (or mlgud-tooltip-echo-area tooltip-use-echo-area (not tooltip-mode)))) (defun mlgud-tooltip-print-command (expr) "Return a suitable command to print the expression EXPR." (pcase mlgud-minor-mode (`gdbmi (concat "-data-evaluate-expression \"" expr "\"")) (`dbx (concat "print " expr)) ((or `xdb `pdb) (concat "p " expr)) (`sdb (concat expr "/")))) (declare-function gdb-input "gdb-mi" (command handler &optional trigger)) (declare-function tooltip-expr-to-print "tooltip" (event)) (declare-function tooltip-event-buffer "tooltip" (event)) (defun mlgud-tooltip-tips (event) "Show tip for identifier or selection under the mouse. The mouse must either point at an identifier or inside a selected region for the tip window to be shown. If `mlgud-tooltip-dereference' is t, add a `*' in front of the printed expression. In the case of a C program controlled by GDB, show the associated #define directives when program is not executing. This function must return nil if it doesn't handle EVENT." (let (process) (when (and (eventp event) mlgud-tooltip-mode mlgud-comint-buffer (buffer-name mlgud-comint-buffer); might be killed (setq process (get-buffer-process mlgud-comint-buffer)) (posn-point (event-end event)) (or (and (eq mlgud-minor-mode 'gdbmi) (not gdb-active-process)) (progn (setq mlgud-tooltip-event event) (eval (cons 'and mlgud-tooltip-display))))) (let ((expr (tooltip-expr-to-print event))) (when expr (if (and (eq mlgud-minor-mode 'gdbmi) (not gdb-active-process)) (progn (with-current-buffer (tooltip-event-buffer event) (let ((define-elt (assoc expr gdb-define-alist))) (unless (null define-elt) (tooltip-show (cdr define-elt) (or mlgud-tooltip-echo-area tooltip-use-echo-area (not tooltip-mode))) expr)))) (when mlgud-tooltip-dereference (setq expr (concat "*" expr))) (let ((cmd (mlgud-tooltip-print-command expr))) (when (and mlgud-tooltip-mode (eq mlgud-minor-mode 'gdb)) (mlgud-tooltip-mode -1) ;; The blank before the newline is for MS-Windows, ;; whose emulation of message box removes newlines and ;; displays a single long line. (message-box "Using MLGUD tooltips in this mode is unsafe \n\ so they have been disabled.")) (unless (null cmd) ; CMD can be nil if unknown debugger (if (eq mlgud-minor-mode 'gdbmi) (if gdb-macro-info (gdb-input (concat "server macro expand " expr "\n") `(lambda () (gdb-tooltip-print-1 ,expr))) (gdb-input (concat cmd "\n") `(lambda () (gdb-tooltip-print ,expr)))) (add-function :override (process-filter process) #'mlgud-tooltip-process-output) (mlgud-basic-call cmd)) expr)))))))) (provide 'mlgud) ;;; mlgud.el ends here matlab-mode/matlab-netshell.el0000664000175000017500000001570514611713120017360 0ustar sebastiensebastien;;; matlab-netshell.el --- Control MATLAB from a network port. ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; Form a back-channale for Emacs to chat with a running MATLAB. ;; Allows you to edit for and with a MATLAB sessions, even if it is not in a ;; matlab-shell buffer. (require 'matlab) ;;; Code: (declare-function matlab-shell-active-p "matlab-shell" ()) (declare-function matlab-shell--get-emacsclient-command "matlab-shell" ()) (declare-function matlab-shell-mode-gud-enable-bindings "matlab-shell-gud" ()) (defvar matlab-netshell-listen-port 32475 "Port used for the Emacs server listening for MATLAB connections.") (defvar matlab-netshell-server-name "*MATLAB netshell*" "Name used for the Netshell server.") (defvar matlab-netshell-clients nil "List of clients created from the MATLAB netshell server.") ;;;###autoload (defun matlab-netshell-server-active-p () "Return non-nil if there is an active MATLAB netshell server." (let ((buff (get-buffer matlab-netshell-server-name))) (when (and buff (get-buffer-process buff)) t))) ;;;###autoload (defun matlab-netshell-server-start () "Start the MATLAB netshell server." (interactive) (make-network-process :name matlab-netshell-server-name :buffer matlab-netshell-server-name :family 'ipv4 :host 'local :service matlab-netshell-listen-port :filter #'matlab-netshell-filter :sentinel #'matlab-netshell-sentinel :server t) (setq matlab-netshell-clients nil) ) (defun matlab-netshell-client () "Return a netshell client." (car matlab-netshell-clients)) (defun matlab-netshell-server-stop nil "Stop the MATLAB Netshell server." (interactive) (dolist (C matlab-netshell-clients) (delete-process C)) (setq matlab-netshell-clients nil) (delete-process matlab-netshell-server-name) ) (defvar matlab-netshell-acc "" "Text Accumulator for MATLAB's netshell.") (make-variable-buffer-local 'matlab-netshell-acc) (defun matlab-netshell-filter (proc string) "Filter used for MATLAB Netshell processes. PROC is the TCP connection that produced STRING." ;; Accumulate from the process (setq matlab-netshell-acc (concat matlab-netshell-acc string)) ;; Wait for a NULL command terminator. (while (string-match "\0" matlab-netshell-acc) (let* ((cmdstr (substring matlab-netshell-acc 0 (match-beginning 0)))) ;; Trim off our new command from accumulator. (setq matlab-netshell-acc (substring matlab-netshell-acc (match-end 0))) ;; Is this a test packet? If so, we're done. (when (not (string= cmdstr "")) ;; Find the newline. (unless (string-match "\n" cmdstr) (message "Unable to find command in MATLAB command. Ignoring.")) (let ((cmd (substring cmdstr 0 (match-beginning 0))) (data (let ((me (match-end 0))) (if (> (length cmdstr) me) (substring cmdstr me) "")))) (matlab-netshell-execute-command proc cmd data) ))))) (defun matlab-netshell-execute-command (proc cmd data) "For MATLAB associated with PROC, execute CMD with DATA. The CMD is a request from MATLAB to do something in Emacs. A common command might be to display data to the user as a response from some Emacs based request." ;; Log the command (with-current-buffer (process-buffer proc) (goto-char (point-max)) (when (not (= (current-column) 0)) (insert "\n")) (insert "Command: [" cmd "]\n") (when (not (string= data "")) (insert "Data: :" data "\n")) ) ;; Interpret the command. (cond ((string= "init" cmd) ;; Make sure GUD bindings are available, but do so in ;; the netshell buffer so when mlgud bundings run, they ;; don't stomp on C-c matlab-mode bindings. (with-current-buffer (process-buffer proc) (matlab-shell-mode-gud-enable-bindings)) ;; Send info about emacs client (when (not (matlab-shell-active-p)) (let* ((ecc (matlab-shell--get-emacsclient-command)) (ecca (if ecc (format "emacs.set('clientcmd', '%s');" ecc)))) (when ecc (matlab-netshell-eval ecca)))) (message "MATLAB connection initialized.") ) ((string= "ack" cmd) (message "Ack recieved. Send ACK back.") (matlab-netshell-send "nowledge" "")) ((string= "nowledge" cmd) (message "Acknowledgement recieved.")) ((string= "output" cmd) (message "Ouput: %S" data)) ((string= "error" cmd) (message "MATLAB Error: %s" data)) ((string= "eval" cmd) ;; (message "MATLAB Evaluating remote request") (let ((forms (read data))) (eval forms))) (t (message "Unknown command from matlab: %S" cmd) ))) (defun matlab-netshell-sentinel (proc msg) "Sentinel used for MATLAB Netshell processes. Identify when a connection is lost, and close down services. PROC is the TCP stream which generated MSG." (cond ((string-match "^open from " msg) ;; New connection - set it up. (setq matlab-netshell-clients (cons proc matlab-netshell-clients)) (let ((newbuff (get-buffer-create (process-name proc)))) (set-process-buffer proc newbuff)) (message "MATLAB Has connected!")) ((string= msg "connection broken by remote peer\n") (setq matlab-netshell-clients (delq proc matlab-netshell-clients)) (message (format "MATLAB has dropped its connecction"))) (t (message "Unhandled event.")))) (defun matlab-netshell-send(cmd data) "Send a command CMD to MATLAB shell connection with DATA." (let ((C (car matlab-netshell-clients))) (if C (process-send-string C (concat cmd "\n" data "\0")) (error "No MATLAB network connection to send to")))) (defun matlab-netshell-eval (mcode) "Send MCODE to the active MATLAB shell connection to eval." (interactive "sMCode: ") (let ((C (car matlab-netshell-clients))) (if C (process-send-string C (concat "eval\n" mcode "\0")) (error "No MATLAB network connection to send to")))) (defun matlab-netshell-evalc (mcode) "Send MCODE to the active MATLAB shell connection to eval." (interactive "sMCode: ") (let ((C (car matlab-netshell-clients))) (if C (process-send-string C (concat "evalc\n" mcode "\0")) (error "No MATLAB network connection to send to")))) (defun matlab-netshell-ack () "Send an ACK to MATLAB to see if it can respond." (interactive) (matlab-netshell-send "ack" "")) ;;(matlab-netshell-server-start) ;;(sleep-for 300) ;;(matlab-netshell-server-stop) (provide 'matlab-netshell) ;;; matlab-netshell.el ends here matlab-mode/README0000664000175000017500000000006414611713120014632 0ustar sebastiensebastienmatlab-emacs project: MATLAB and Emacs integration matlab-mode/matlab-topic.el0000664000175000017500000001671014611713120016655 0ustar sebastiensebastien;;; matlab-topic.el --- Help browsing via Emacs buffers. ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; Browsing topics via the MATLAB shell in an Emacs buffer is a nice ;; feature. Moving as optional as this is rarely used. ;;; Code: (require 'matlab) (require 'matlab-shell) (require 'view) (defvar matlab-shell-topic-mouse-face-keywords '(;; These are subtopic fields... ("^\\(\\w+/\\w+\\)[ \t]+-" 1 font-lock-constant-face) ;; These are functions... ("^[ \t]+\\(\\w+\\)[ \t]+-" 1 font-lock-function-name-face) ;; Here is a See Also line... ("[ \t]+See also " ("\\(\\w+\\)\\([,.]\\| and\\|$\\) *" nil nil (1 font-lock-constant-face)))) "These are keywords we also want to put mouse-faces on.") (defvar matlab-shell-topic-font-lock-keywords (append matlab-shell-topic-mouse-face-keywords '(("^[^:\n]+:$" 0 font-lock-keyword-face) ;; These are subheadings... ("^[ \t]+\\([^.\n]+[a-zA-Z.]\\)$" 1 'underline) )) "Keywords useful for highlighting a MATLAB TOPIC buffer.") (defvar matlab-shell-help-font-lock-keywords (append matlab-shell-topic-mouse-face-keywords '(;; Function call examples ("[ \t]\\([A-Z]+\\)\\s-*=\\s-*\\([A-Z]+[0-9]*\\)(" (1 font-lock-variable-name-face) (2 font-lock-function-name-face)) ("[ \t]\\([A-Z]+[0-9]*\\)(" (1 font-lock-function-name-face)) ;; Parameters: Not very accurate, unfortunately. ("[ \t]\\([A-Z]+[0-9]*\\)(" ("'?\\(\\w+\\)'?\\([,)]\\) *" nil nil (1 font-lock-variable-name-face)) ) ;; Reference uppercase words ("\\<\\([A-Z]+[0-9]*\\)\\>" 1 font-lock-constant-face))) "Keywords for regular help buffers.") ;; View-major-mode is an emacs20 thing. This gives us a small compatibility ;; layer. (eval-and-compile (if (not (fboundp 'view-major-mode)) (defalias 'view-major-mode 'view-mode))) (defvar matlab-shell-help-mode-map (let ((km (make-sparse-keymap))) (define-key km [return] 'matlab-shell-help-choose) (define-key km "q" 'bury-buffer) (define-key km [(control h) (control m)] matlab-help-map) (if (string-match "XEmacs" emacs-version) (define-key km [button2] 'matlab-shell-help-click) (define-key km [mouse-2] 'matlab-shell-help-click) (define-key km [mouse-1] 'matlab-shell-help-click) ) (set-keymap-parent km view-mode-map) km) "Keymap used in MATLAB help mode.") (easy-menu-define matlab-shell-help-mode-menu matlab-shell-help-mode-map "MATLAB shell Help menu" '("MATLAB Help" ["Describe This Command" matlab-shell-help-choose t] "----" ["Describe Command" matlab-shell-describe-command t] ["Describe Variable" matlab-shell-describe-variable t] ["Command Apropos" matlab-shell-apropos t] "----" ["Exit" bury-buffer t])) ;; Need this to fix wierd problem in define-derived-mode (defvar matlab-shell-help-mode-syntax-table (make-syntax-table) "Syntax table used in `matlab-shell-help-mode'.") ;;;###autoload (define-derived-mode matlab-shell-help-mode view-major-mode "M-Help" "Major mode for viewing MATLAB help text. Entry to this mode runs the normal hook `matlab-shell-help-mode-hook'. Commands: \\{matlab-shell-help-mode-map}" :syntax-table matlab-shell-help-mode-syntax-table (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((matlab-shell-help-font-lock-keywords) t nil ((?_ . "w")))) ;; This makes sure that we really enter font lock since ;; kill-all-local-variables is not used by old view-mode. (and (boundp 'global-font-lock-mode) global-font-lock-mode (not font-lock-mode) (font-lock-mode 1)) (matlab-shell-help-mouse-highlight-subtopics) (font-lock-ensure) ) (defun matlab-shell-help-click (e) "Click on an item in a MATLAB help buffer we want more information on. Must be bound to event E." (interactive "e") (mouse-set-point e) (matlab-shell-help-choose)) (defun matlab-shell-help-choose () "Choose the help to expand on that is under the cursor. This can fill the help buffer with new information. If the help is a command, use `matlab-shell-describe-command' instead of changing the help buffer." (interactive) (let ((topic nil) (fun nil) (p (point))) (save-excursion (beginning-of-line) (if (looking-at "^\\w+/\\(\\w+\\)[ \t]+-") (setq topic (match-string 1)) (if (looking-at "^[ \t]+\\(\\(\\w\\|_\\)+\\)[ \t]+-") (setq fun (match-string 1)) (if (and (not (looking-at "^[ \t]+See also")) (not (save-excursion (forward-char -2) (looking-at ",$")))) ;;(error "You did not click on a subtopic, function or reference") nil (goto-char p) (forward-word -1) (if (not (looking-at "\\(\\(\\w\\|_\\)+\\)\\([.,]\\| and\\|\n\\)")) ;;(error "You must click on a reference") nil (setq topic (match-string 1))))))) ;;(message "Opening item %s..." (or topic fun)) (when (or fun topic) (matlab-shell-describe-command (downcase (or fun topic)))) )) (defun matlab-shell-help-mouse-highlight-subtopics () "Put a `mouse-face' on all clickable targets in this buffer." (save-excursion (let ((el matlab-shell-topic-mouse-face-keywords) (inhibit-read-only t)) (while el (goto-char (point-min)) (while (re-search-forward (car (car el)) nil t) (let ((cd (car (cdr (car el))))) (if (numberp cd) (put-text-property (match-beginning cd) (match-end cd) 'mouse-face 'highlight) (while (re-search-forward (car cd) nil t) (put-text-property (match-beginning (car (nth 3 cd))) (match-end (car (nth 3 cd))) 'mouse-face 'highlight))))) (setq el (cdr el)))))) (defvar mouse-grabbed-buffer) ;; Suppress compilation warning in Emacs (an XEmacs only variable) (defvar mode-motion-extent) ;; Suppress compilation warning in Emacs (an XEmacs only variable) (defun matlab-shell-topic-highlight-line (event) "A value of `mode-motion-hook' which will highlight topics under the mouse. EVENT is the user mouse event." ;; XEMACS only function (let* ((buffer (when (fboundp 'event-buffer) (event-buffer event))) (point (and buffer (when (fboundp 'event-point) (event-point event))))) (if (and buffer (not (eq buffer mouse-grabbed-buffer))) (save-excursion (save-window-excursion (set-buffer buffer) (when (fboundp 'mode-motion-ensure-extent-ok) (mode-motion-ensure-extent-ok event)) (if (not point) (when (fboundp 'detach-extent) (detach-extent mode-motion-extent)) (goto-char point) (end-of-line) (setq point (point)) (beginning-of-line) (if (or (looking-at "^\\w+/\\(\\w+\\)[ \t]+-") (looking-at "^[ \t]+\\(\\(\\w\\|_\\)+\\)[ \t]+-")) (when (fboundp 'set-extent-endpoints) (set-extent-endpoints mode-motion-extent (point) point)) (when (fboundp 'detach-extent) (detach-extent mode-motion-extent))))))))) (provide 'matlab-topic) ;;; matlab-topic.el ends here matlab-mode/semanticdb-matlab.el0000664000175000017500000003235014611713120017646 0ustar sebastiensebastien;;; semanticdb-matlab.el --- Semantic database extensions for MATLAB ;;; Copyright (C) 2008, 2012, 2013, 2019 David Engster ;; Author: David Engster ;; based heavily on semanticdb-skel.el (C) Eric Ludlam ;; This file is not part of GNU Emacs. ;; This 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, or (at your option) ;; any later version. ;; This software 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Support for Semantic Databases for MATLAB buffers. ;; For generic function searching. (require 'eieio) (require 'eieio-opt) ;; eval-and-compile needed because of the condition-case. ;; Normally a require is eval'd during compile, so this is the same. (eval-and-compile (condition-case nil (require 'semanticdb) (error (require 'semantic/db)))) (eval-and-compile (require 'matlab) (require 'matlab-shell)) ;;; Code: ;; Put all directories which should be recursively scanned for your ;; personal MATLAB files here. (defvar semanticdb-matlab-include-paths (if (file-exists-p (expand-file-name "~/matlab")) (list (expand-file-name "~/matlab") ;; Default location for extra code. ) ;; Else, no default path. nil) "Directories which should be scanned for m-files.") ;;; Classes: (defclass semanticdb-table-matlab (semanticdb-search-results-table) ((major-mode :initform matlab-mode) ) "A table for returning search results from MATLAB path.") (defclass semanticdb-project-database-matlab (semanticdb-project-database ;; Use SINGLETON if there should be only one copy of this database. ;; Do not use this if you need a different copy for different projects. ;; eieio-singleton ) ((new-table-class :initform semanticdb-table-matlab :type class :documentation "New tables created for this database are of this class.") ) "Database representing MATLAB path.") ;; Create the database, and add it to searchable databases for matlab mode. (defvar-mode-local matlab-mode semanticdb-project-system-databases (list (make-instance 'semanticdb-project-database-matlab)) "Search MATLAB path for symbols.") ;; NOTE: Be sure to modify this to the best advantage of your ;; language. (defvar-mode-local matlab-mode semanticdb-find-default-throttle '(project omniscience) "Search project files, then search this omniscience database. It is not necessary to to system or recursive searching because of the omniscience database.") ;;; Filename based methods ;; (cl-defmethod semanticdb-get-database-tables ((obj semanticdb-project-database-matlab)) "For a MATLAB database OBJ, there are no explicit tables. Create one of our special tables that can act as an intermediary." ;; NOTE: This method overrides an accessor for the `tables' slot in ;; a database. You can either construct your own (like tmp here ;; or you can manage any number of tables. ;; 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 (make-instance 'semanticdb-table-matlab))) (oset obj tables (list newtable)) (oset newtable parent-db obj) (oset newtable tags nil) )) (cl-call-next-method)) (cl-defmethod semanticdb-file-table ((obj semanticdb-project-database-matlab) filename) "From OBJ, return FILENAME's associated table object." ;; NOTE: See not for `semanticdb-get-database-tables'. (car (semanticdb-get-database-tables obj)) ) (cl-defmethod semanticdb-get-tags ((table semanticdb-table-matlab )) "Return the list of tags belonging to TABLE." ;; NOTE: Omniscient databases probably don't want to keep large tabes ;; lolly-gagging about. Keep internal Emacs tables empty and ;; refer to alternate databases when you need something. nil) (cl-defmethod semanticdb-equivalent-mode ((table semanticdb-table-matlab) &optional buffer) "Return non-nil if TABLE's mode is equivalent to BUFFER. Equivalent modes are specified by by `semantic-equivalent-major-modes' local variable." (with-current-buffer buffer (eq (or mode-local-active-mode major-mode) 'matlab-mode))) (cl-defmethod semanticdb-full-filename ((obj semanticdb-table-matlab)) "Fetch the full filename that OBJ refers to. This function is currently a stub." ;; FIXME ;; return filename for object - what should we do with builtin functions? nil) ;;; Usage ;; ;; Unlike other tables, an omniscent database does not need to ;; be associated with a path. Use this routine to always add ourselves ;; to a search list. (define-mode-local-override semanticdb-find-translate-path matlab-mode (path brutish) "Return a list of semanticdb tables asociated with PATH. If brutish, do the default action. If not brutish, do the default action, and append the system database (if available.)" (let ((default ;; When we recurse, disable searching of system databases ;; so that our MATLAB database only shows up once when ;; we append it in this iteration. (let ((semanticdb-search-system-databases nil) ) (if (fboundp 'semanticdb-find-translate-path-default) (semanticdb-find-translate-path-default path brutish) (error "Variable semanticdb-find-translate-path-default doesn't exist") )))) ;; Don't add anything if BRUTISH is on (it will be added in that fcn) ;; or if we aren't supposed to search the system. (if (or brutish (not semanticdb-search-system-databases)) default (let ((tables (apply #'append (mapcar (lambda (db) (semanticdb-get-database-tables db)) semanticdb-project-system-databases)))) (append default tables))))) ;;; Search Overrides ;; ;; NOTE WHEN IMPLEMENTING: Be sure to add doc-string updates explaining ;; how your new search routines are implemented. ;; (defvar semanticdb-matlab-system-files-cache '(nil) "Internal cache for system M files. This variable caches all M files in the directories listed in `semantic-matlab-system-paths-include' under MATLAB root directory. Users can reset this cache using `semanticdb-matlab-reset-files-cache'") (defvar semanticdb-matlab-user-files-cache '(nil) "Internal cache for user M files. This variable caches all M files in the directories listed in `semanticdb-matlab-include-paths'. Users can reset this cache using `semanticdb-matlab-reset-files-cache'.") (defvar semanticdb-matlab-user-class-cache nil "Internal cache for user classes.") (defun semanticdb-matlab-reset-files-cache () "Reset semanticdb-matlab file cache." (interactive) (setq semanticdb-matlab-user-files-cache '(nil)) (setq semanticdb-matlab-system-files-cache '(nil))) (defun semanticdb-matlab-possibly-add-buffer-to-cache () "Add current buffer file name to cache. This function will add the current buffer file name to `semanticdb-matlab-user-files-cache' if not already there. Meant to be called in local `after-save-hook'." (unless (and semanticdb-matlab-user-files-cache (member (buffer-file-name) (cdr semanticdb-matlab-user-files-cache))) (setcdr semanticdb-matlab-user-files-cache (append (cdr semanticdb-matlab-user-files-cache) (list (buffer-file-name)))))) ;; Make sure newly created MATLAB files get in the user-files-cache (add-hook 'matlab-mode-hook (lambda () ;; add buffer-local after-save-hook (add-hook 'after-save-hook 'semanticdb-matlab-possibly-add-buffer-to-cache t t))) ;; Helper functions (defun semanticdb-matlab-scan-directories (dirs &optional recursive exclude-classes exclude-private) "Get list of all m-files in DIRS. DIRS is a list of directories. If RECURSIVE, every subdirectory will be included in the search. If EXCLUDE-CLASSES, class directories (beginning with '@') will be skipped. If EXCLUDE-PRIVATE, 'private' directories will be skipped." (if dirs (let (files) (dolist (dir dirs) (let (subdirs) (dolist (cur (directory-files dir t "[^.]" t)) (if (file-directory-p cur) (when (and recursive (not (and exclude-classes (string-match ".*/@" cur))) (not (and exclude-private (string-match ".*/private$" cur)))) (push cur subdirs)) (when (string-match "\\.m$" cur) (push cur files)))) (when subdirs (setq files (append files (semanticdb-matlab-scan-directories subdirs recursive exclude-classes exclude-private)))))) files) nil)) (defvar semantic-matlab-dependency-system-include-path) ;; quiet compiler warning (defun semanticdb-matlab-cache-files () "Cache user and system MATLAB files if necessary." ;; car of *-file-cache variables is used as flag (unless (car semanticdb-matlab-system-files-cache) (setq semanticdb-matlab-system-files-cache (cons t (semanticdb-matlab-scan-directories semantic-matlab-dependency-system-include-path t t t)))) (unless (car semanticdb-matlab-user-files-cache) (setq semanticdb-matlab-user-files-cache (cons t (semanticdb-matlab-scan-directories semanticdb-matlab-include-paths t nil nil))) )) (defun semanticdb-matlab-find-name (name &optional type) "Find NAME in matlab file names. If TYPE is 'regex, NAME is a regular expression. If TYPE is 'prefix, NAME is a prefix." (semanticdb-matlab-cache-files) (let ((files (append (cdr semanticdb-matlab-system-files-cache) (cdr semanticdb-matlab-user-files-cache))) regexp results) (cond ((eq type 'prefix) (setq regexp (format "^%s.*\\.m$" name))) ((eq type 'regex) (setq regexp (format "%s\\.m$" name))) (t (setq regexp (format "^%s\\.m" name)))) (dolist (cur files) (when (string-match regexp (file-name-nondirectory cur)) (push cur results))) results)) (define-mode-local-override semantic-ctxt-current-class-list matlab-mode (point) "Return a list of tag classes that are allowed at point. If point is nil, the current buffer location is used." (cond ((looking-at ".+=") '(variable type)) ((looking-back "\\(get\\|set\\)([a-zA-Z_0-9]*" nil) '(variable type)) ((looking-back "\\(get\\|set\\)([a-zA-Z_0-9]+,'[a-zA-Z_0-9]*" nil) '(variable)) ((looking-back "\\.[a-zA-Z_0-9]*" nil) '(variable)) ((looking-at "\\s-*([a-zA-Z_0-9]+,") '(function)) (t '(function variable type)))) ;; Search functions (cl-defmethod semanticdb-find-tags-by-name-method ((table semanticdb-table-matlab) name &optional tags) "Find all tags named NAME in TABLE. Return a list of tags." ;; If we have tags, go up. (if tags (cl-call-next-method) (let (where) ;; If MATLAB shell is active, use it. (when (and (matlab-shell-active-p) (setq where (matlab-shell-which-fcn name))) (when (and (not (file-exists-p (car where))) ;; Sometimes MATLAB builtin functions lie. (string-match "@" (car where))) (setq where (list (concat (substring (car where) 0 (match-beginning 0)) name ".m"))))) (unless (car where) ;; Fall back to home-made database. (setq where (list (car (semanticdb-matlab-find-name name))))) (if (car where) (list (car (semanticdb-file-stream (car where)))) nil)))) (cl-defmethod semanticdb-find-tags-by-name-regexp-method ((table semanticdb-table-matlab) regex &optional tags) "Find all tags with name matching REGEX in TABLE. Optional argument TAGS is a list of tags to search. Return a list of tags." (if tags (cl-call-next-method) (let ((files (semanticdb-matlab-find-name regex 'regex))) (delq nil (mapcar #'(lambda (x) (car (semanticdb-file-stream x))) files))))) (cl-defmethod semanticdb-find-tags-for-completion-method ((table semanticdb-table-matlab) prefix &optional tags) "In TABLE, find all occurances of tags matching PREFIX. Optional argument TAGS is a list of tags to search. Returns a table of all matching tags." ;; If we have tags, go up. (if tags (cl-call-next-method) ;; first, get completions from home-made database... (let ((compdb (semanticdb-matlab-find-name prefix 'prefix)) compshell) ;; ...and from MATLAB shell, if available (when (matlab-shell-active-p) (setq compshell (mapcar (lambda (x) (when (stringp x) (let ((where (matlab-shell-which-fcn (car x)))) ;; correct name for builtin functions (when (and (cdr where) (string-match "\\(.*\\)/@.*\\(/[A-Za-z_0-9]+\\.m\\)" (car where))) (setq where (list (concat (match-string 1 (car where)) (match-string 2 (car where)))))) (list (car where))))) (matlab-shell-completion-list prefix))) ;; combine results (mapc (lambda (x) (unless (member x compdb) (setq compdb (append compdb x)))) compshell)) ;; generate tags (delq nil (mapcar #'(lambda (x) (car (semanticdb-file-stream x))) compdb))))) (provide 'semanticdb-matlab) ;;; semanticdb-matlab.el ends here matlab-mode/ChangeLog.old10000664000175000017500000012614014611713120016366 0ustar sebastiensebastienThis is the old-style ChangeLog before integration into CVS @ The MathWorks ------- 30Jul01 by Eric Ludlam Better doc for matlab-mode & stringify. Fix `matlab-fill-paragraph' so comments do not bleed into the previous line. 04Jun01 by Eric Ludlam Version change to 2.2.4.alpha 07May01 by Eric Ludlam Set `indent-tabs-mode' to nil for M files. Tabs are not allowed for V6. 05Apr01 by Eric Ludlam Fixed auto-fill for code to add end brackets after a string if the string is already complete. 16Feb01 by Stefan Holst Add ./ to list of highlighted math operators. 15Dec00 by Peter Boettcher hacked by Eric Ludlam Add local binding to `matlab-mode' for `add-log-current-defun-function'. New function `matlab-match-function-re' to match functions. New function `matlab-current-defun' gets the name of the current fn. In `matlab-mode-vf-functionname' use `matlab-match-function-re'. Added @ for file names to regexp `gud-matlab-error-regexp'. 27Oct00 by Volker Franz Add text to `comint-input-ring' in `matlab-shell-save-and-go'. 10Oct00 by Eric Ludlam When using a value from `matlab-maximum-indents', enable the maximum to be offset by the current indentation level. 08Sep00 by Eric Ludlam Fixed numeric ij highlighting. Fixed `matlab-comment-on-line' for case when only % is inside a string. 08Jun00 by Eric Ludlam Added menu item for `matlab-uncomment-region'. 06Jun00 by Eric Ludlam Added `matlab-shell-input-ring-size' as a variable. 30May00 by Eric Ludlam `matlab-beginning-of-command' now skips past comments which are continuations from the previous line. Fixed `matlab-defun-regex' to require real whitespace after the function keyword. 18Apr00 by Eric Ludlam Finally: Version 2.2.3. 13Apr00 by Eric Ludlam with original from Hubert Selhofer Added `matlab-find-unreachable-code' to detect if (0) situations. Use this in font lock to turn that code into comment colors. Update `matlab-forward-sexp' to take universal argument. When set, stop on else/case/catch blocks. Used by above fn to not highlight ELSE clause. Version 2.2.3.b4 06Apr00 by Eric Ludlam Updated `matlab-mode-vf-quietafy-buffer' to remove trailing whitespace too. Make sure the `fume' vairables exist before modifying them. 08Mar00 by Adrie Dane tweeked by Eric M. Ludlam Added support for XEmacs' func-menu program. 19Jan00 by Eric M. Ludlam Fixed typo in C-c: binding to uncomment region. 19Jan00 by Hubert Selhofer Font-lock complex numbers (i,j). 12Jan00 by Eric M. Ludlam idea by Jinwei Shen Added `matlab-show-matlab-shell-buffer' to display the shell from a matlab buffer. I moved commands that affect the shell from a text buffer into their own section. 29Oct99 by Eric M. Ludlam `matlab-shell-command-switches' is now a custom string type. Version 2.2.3.b2 04Aug99 by Eric M. Ludlam Customize new font lock keyword list variables. Fixed `matlab-lattr-middle-block-cont' to return 0 if there IS a middle block that also contains an END on the same line. 03Aug99 by Hubert Selhofer augmented by Eric M. Ludlam Use Emacs 20's `line-end-position' and `line-beginning-position'. Use Emacs 20's `regexp-opt' when generating font-lock keywords. Version 2.2.3.b1 23July99 by Peter J. Acklam Font Lock keywords for function defs w/ special EOL. 02July99 by Eric Ludlam Added `matlab-uncomment-region' command bound to C-c : 04June99 by Ian Wells Updated `gud-matlab-error-regexp' to work w/ windows filenames. 20May99 by Eric Ludlam Updateded the ftp site where you can get matlab.el 21Apr99 by Eric Ludlam Added menu items to toggle code-filling, and periodic details. Update version to 2.2.2. 14Apr99 by Eric Ludlam Added `matlab-comment-line-break-function' on M-j for compatibility. 12Apr99 by Eric Ludlam In `matlab-valid-end-construct-p' do not use end-of-command. The end of line command seems to work just fine. 31Mar99 by Eric Ludlam Fixed `matlab-end-of-command' to have a better short-cicuit for long buffers. (depends on block commands not appearing in matrix) 04Mar99 by Eric Ludlam Fixed `matlab-end-of-command' to be savvy of eob. Fixed `matlab-indent-end-before-ret' to correctly use the regex functions that need re-indenting backwards. Use of `matlab-block-beg-re' was just wrong. 01Mar99 by Eric Ludlam Removed ' from menu bindings toggling variables. 10Feb99 by Eric Ludlam `matlab-navigation-syntax' will ALSO set `case-fold-search' to nil. 09Feb99 by Eric Ludlam Fixed `matlab-beginning-of-command' to not stall inside a matrix inside a continuation. 29Jan99 by Eric Ludlam Added try/catch to hilit19 keywords. 28Jan99 by Eric Ludlam Fixed `matlab-indent-end-before-ret' to use `matlab-block-beg-re' instead of a home-grown regular expression. 17Dec98 by Eric Ludlam Updated some documentation. Post 2.2.1 16Dec98 by Eric Ludlam Do not require font lock. Set up font-lock hooks to generate our special faces when font lock is loaded (if it isn't already) Add require 'gud on compile. Added `matlab-handle-simulink'. When set to nil, font lock keywords are modified to remove simulink special stuff. Fixed `matlab-cursor-in-string' so that the `incomplete' param works with strings w/ '' in them. Added celeron to `matlab-mode-vf-quietafy-buffer', and also added a speed optimization when calling `matlab-end-of-command'. 07Dec98 by Eric Ludlam Added `matlab-maximum-indents' to hold indentation maximum data. Update `matlab-calculate-indentation' to use the maximums variable to allow custom indentation levels. Also fixed so that if a given paren is immediatly followed by EOL, or if an indent past arg1's 1st arg is followed by and EOL, then backup one indentation type. 04Dec98 by Eric Ludlam In `gud-matlab-find-file', don't use replace-match on a string. Fixed nasty bug when a paren appears in a string in an array. Added `matlab-up-list' which only works backwards at the moment. Added `array-cont' and `array-end' indentation types. Array-end types will line up with the opening bracket, not the array elements. Hacked several fns to use `array-cont's restrict parameter to speed things up. 03Dec98 by Eric Ludlam Moved simulink keywords into a guadier level. Added require font-lock. Fixed `matlab-ltype-help-comm' to not loop endlessly @ bob. Added `matlab-ispell-strings-region', `matlab-ispell-strings', and `matlab-ispell-comments'. Added to menu & keymap. 02Dec98 by Eric Ludlam Fixed `matlab-show-line-info' to not use old indent functions. Fixed "proc" ref in `matlab-shell-send-string' which only worked incidentally. Release 2.2 15Nov98 by Eric Ludlam When auto-filling comments at the end of a line, use auto-fill, and not fill-paragraph. `matlab-set-comm-fill-prefix' no longer grabs preceding spaces. 12Nov98 by Eric Ludlam When a regular matlab prompt appears, turn off the overlay arrow. 11Nov98 by Eric Ludlam Attempted to make `matlab-shell-echos' disply the string about to be run for save-and-go, and run-region in the fn `matlab-shell-send-string' 10Nov98 by Eric Ludlam Fixed bug in `gud-matlab-marker-filter' so that instead of checking for specific prompts, we check specifically for the error prefix, and display everything else (which includes the prompt of the INPUT command. 04Nov98 by Eric Ludlam Fixed `matlab-shell-echos' -> `matlab-shell-echoes'. 04Nov98 by Eric Ludlam Post 2.1.4d. 03Nov98 by Eric Ludlam Added `matlab-shell-echos' for NT engine program. 02Nov98 by Eric Ludlam Due to new engine program for NT, be careful w/ error message in `matlab-shell'. 31Oct98 by Matt Wette changed local variable `comment-column' to value of `matlab-comment-column' (not the symbol) changed reference of `matlab-comment-column' to `comment-column'. Fixed matlab-comment on lines w/ comment. 26Oct98 by Eric Ludlam Added help for `matlab-shell' on Windows platform. 23Oct98 by Eric Ludlam Fixed font locking for statement vars for an embedded fn call. Fixed font locking non-simulink commands starting w/ `sim' 19Oct98 by Eric Ludlam When auto-filling, ignore lines which are commented regions. 16Oct98 by Eric Ludlam In `matlab-set-comm-fill-prefix', account for the $$$ of a region comment. 14Oct98 by Eric Ludlam When debugging source, convert .p files into .m files before loading. 07Oct98 by Eric Ludlam A bunch of changes to enable GUD usage under XEmacs 20.4. Updated some documentation. Switched to 2.1.4 due to changes. 06Oct98 by Eric Ludlam Added binding for `backspace' symbol to not delete prompt. 02Oct98 by Eric Ludlam Fixed matlab shell to beep shell to beep when filtering C-g Added a command to prevent simple deletion of the prompt in shell mode. 30Sept98 by Eric Ludlam Post 2.1.3i to mailing list. 25Sept98 by Eric Ludlam Fixed a region bug for Emacs 20.2. Apparently `region-face' is important, and I shouldn't have used it as a local variable. 24Sept98 by Eric Ludlam Fixed bug for arg1 functions when 1st arg is a function call, and the lineup code matches past arg1 of this fn. Added `matlab-arg1-max-indent-length' to control how long the first argument must be before we stop indenting. 23Sept98 by Eric Ludlam Made sure region was correctly found for XEmacs users w/ custom. 22Sept98 by Eric Ludlam Added optional arg REINDENT to `matlab-insert-end-block' to reindent the code affected by the new block. 10Sept98 by Eric Ludlam Fixed use of `matlab-indent-past-arg1-functions' so that the `_' char is also skipped over when backing up. The font lock keywords for 1st arg is var functions now uses `matlab-indent-past-arg1-functions' as the default reference. Added a bunch of simulink keywords to font lock, included a simulink keyword face, and to `matlab-indent-past-arg1-functions'. 09Sept98 by Eric Ludlam Added waitfor in with set/get as a handle/property pair acceptor. Fixed defcustom rules for for `matlab-unterminated-string-face' to take the same properties as `font-lock-string-face'. Added some really gaudy keywords for highlighting interesting things in a file's help comment. 31Aug98 by Eric Ludlam Post 2.1.3f to mailing list. Fixed typo in `matlab-shell' relating to keymaps for older emacsen. 21Aug98 by Eric Ludlam Fixed `matlab-ltype-help-comm' to not hang at beginning of buffer. Add `matlab-ltype-comm-ignore' returns t on lines which were hit with `matlab-comment-region'. `matlab-prev-line' ignores these when scanning backwards, and `matlab-calculate-indentation' forces these lines to indent to column 0. 20Aug98 by Eric Ludlam Update doc for `matlab-case-level' to include cons cell Update `matlab-calc-indent' and `matlab-next-line-indentation' to handle cons cell for `matlab-case-level'. 19Aug98 by Eric Ludlam Removed docs about 'nohelpcomment. This is the default now. 18Aug98 by Eric Ludlam Fixed `matlab-find-convenient-line-break' to break before a function name, not instead of at it's opening parenthisis. 14Aug98 by Eric Ludlam Fixed `matlab-auto-fill' so that when filling many parameters of strings, the small confusion of string continues doesn't mess things up (by inserting brackets and things like that.) Added `matlab-indent-past-arg1-functions' regex which permits indentation of continuations after certain classes of functions to occur with the second argument. Updated `matlab-set-comm-fill-prefix' to be smaller, and smarter. This eliminated `matlab-set-comm-fill-prefix-post-code' Removed the unused `matlab-set-code-fill-prefix'. Fixed `matlab-auto-fill' to fill post code comments first when applicable. Fixed `matlab-fill-paragraph' so that if the cursor is AFTER the ..., it will fill in code mode, not comment mode. Also, trailing comments are correctly decommented. 12Aug98 by Eric Ludlam Rewrote `matlab-fill-region' to go through the region on a command by command basis calling `matlab-fill-paragraph' Fixed `matlab-fill-paragraph' to justify code lines, and insert spaces wher the line breaks were. Deleted `matlab-justify-comment-line' since we can get that behavior built in. Added `matlab-fill-fudge-hard-maximum' to be the hard maximum limit when filling code. Fixed `matlab-beginning-of-command' to correctly handle continued lines at the beginning of a buffer. Fixed `matlab-calc-indent' to be better about indentation at the beginning of a buffer. Fixed `matlab-find-convenient-line-break' to be smarter about varios potential problems including double-fudge parens, and before/after space search short-circuiting. 31Jul98 by Eric Ludlam Added `matlab-fill-fudge' which is a fuzzy fill zone. Also added `matlab-find-convenient-line-break' which calculates a fuzzy column to break a line on. Updated `matlab-do-autofill' to use the above function. Updated `matlab-fill-paragraph' to use the new fuzzy line-break calculation on code. Added `matlab-ltype-continued-comm' to identify the column on a previous line where the comment column is. Updated indentor to indent comments indentically to the previous line's comment column even if it follows code. Updated `matlab-beginning-of-command' and `matlab-end-of-command' to correctly trace to the end of a command's comments. 29Jul98 by Eric Ludlam Fixed `matlab-auto-fill' to prevent filling if typing a space while the cursor is on the first . or the elipsis. Fixed `matlab-ltype-endfunction-comm' so it won't barf on end-of- file conditions. 27Jul98 by Eric Ludlam Fixed first code line in a function indenting problem. The variable `matlab-indent-function' was ignored, where code after a help comment was always indented, and code after the first function decl, but w/ no help was always not indented. Fixed `matlab-beginning-of-command' to correctly handle the first line in a file containing continuation marks. Fixed auto fill to not fill lines with elipses on them already. Fixed `matlab-valid-end-construct-p' to handle unterminated lists or matrixes. If a list is unterminated, then all ends are assumed to be invalid. 20Jul98 by Eric Ludlam Check to make sure `set-keymap-parent' exists before using it. 07Jul98 by Eric Ludlam Moved `matlab-mode-verify-fix-functions' to custom variable section, and turned it into a defcustom declaration. Added `matlab-lattr-array-cont' function to identify lines that have unstarted/ended arrays in them. `matlab-beginning-of-command' and `matlab-end-of-command' now track across unbounded array constants. 06Jul98 by Eric Ludlam Put `matlab-indent-function' back to nil. `matlab-mode-vf-quietafy-buffer' can take optional ignored FAST arg. Release 2.1.2. 02Jul98 by Eric Ludlam Added `matlab-mode-vf-quietafy-buffer' which finds lines which may produce ANS style output. When `matlab-indent-function' is 'nohelpcomment, then the first comment in a function is unindented while the function body is indented. 22Jun98 by Eric Ludlam Even more string highlighting fixes for spaces in strings. 17Jun98 by Eric Ludlam Re-worked the string/comment font locking to use functions instead of string matching. The functions are now much more accurate. 16Jun98 by Eric Ludlam Fixed auto-fill prefix for comments to not require a space. 15Jun98 by Eric Ludlam More string font lock fixing comments directly after a string. 10Jun98 by Eric Ludlam Fixed unterminated strings that contain comment characters, and don't highlight the comments in unterminated strings. Discovered why XEmacs 20 wouldn't highlight some items, and fixed it. Required lots of 'appends, plus the removal of 'prepend for comments which allowed other keywords to show through. 10Jun98 by Peter J. Acklam Fixed unterminated strings with quoted quotes. 05Jun98 by Stelios Kyriacou Update `gud-matlab-error-regexp' to handle warnings. Update `matlab-shell-last-error' to find errors the cursor is in. 04Jun98 by Eric Ludlam Attempt to make `matlab-unterminated-string-face' do the defface thing. 04Jun98 by Stelios Kyriacou Fixed `matlab-find-file-on-path' to correctly search the list of paths. Added check that a path is a directory before fetching it's files. Fixed a bug when I merged a change in from jacklam which I seem to have forgotten to put into this history. 03Jun98 by Eric Ludlam `matlab-unterminated-string-face' is now a self-referencing variable. Post version 2.1.1 02Jun98 by Eric Ludlam Fixed the function `matlab-mode-determine-mfile-path' to not fail. Updated `matlab-find-file-on-path' to handle nil's in the list and provide helpful errors 01Jun98 by Eric Ludlam Post version 2.1 27May98 by Eric Ludlam Enabled `matlab-mode-determine-mfile-path' and used it to define the variable `matlab-mode-install-path'. This is then used by the new commands `matlab-find-file-on-path' and `matlab-find-file-click' Added these to the keymap and meny. 22May98 by Dan Nicolaescu Fixed derived modes to correctly font lock upon creation. 19May98 by Peter J. Acklam New function highlighting regexps which are more accurate. 11May98 by Eric M. Ludlam Ran new checkdoc on the file and fixed all calls to `error' 11May98 by Peter J. Acklam Fixed a string highlighting bug. 11May98 Michael Granzow Found bug in `matlab-keywords-boolean'. 08May98 by Eric M. Ludlam CR after unterminated END will error, but still insert the CR. 08May98 by Hubert Selhofer CR when (point) == (point-min) no longer errors 05May98 by Hubert Selhofer Many spelling fixes in comments, and doc strings. Adjusted some font-lock keywords to be more compact/effecient. 30Apr98 by Eric M. Ludlam %endfunction unindenting can now have arbitrary text after it. 24Apr98 by Peter J. Acklam Fixed highlighting of for statements w/ traling comments. 23Apr98 by Eric M. Ludlam Fixed -vf-block functions to have more restrictive before-keyword so we don't accidentally match keywords at the end of symbols. 22Apr98 by Eric M. Ludlam Release 2.0 to web site and newsgroups. Ran checkdoc/ispell on entire file. Cleaned up some compile-time warnings. Verified XEmacs compatibility. 13Apr98 by Eric M. Ludlam Fixed bug in `matlab-mode-vf-functionname' to prevent infinite loop on empty files. 10Apr98 by Eric M. Ludlam Added break to highlighted keywords. Case variable highlighting now stops at comment endings. 07Apr98 by Eric M. Ludlam `matlab-ltype-comm' no longer demands a space after the %. Indentor now unindents the comment %endfunction. Removed transposing transpose part. It broke quoted quotes. 02Apr98 by Eric M. Ludlam Comments appearing at the end of a function, and just before a new subfunction, are now unintented if `matlab-indent-function' is non-nil. This lets matlab users use %endfunction at the end of a function, and get the indentation right. 01Apr98 by Eric M. Ludlam Smarter font lock for case (jacklam@math.uio.no) Auto fill accounts for chars inserted based on the variable `matlab-fill-count-ellipsis-flag'. Auto fill will now fill a string by putting it into brackets controlled by `matlab-fill-strings-flag'. 18Mar98 by Peter J. Acklam Enabled multi-line function definitions in font-lock and imenu. 16Mar98 by Eric M. Ludlam Fixed potential error in comment searching around ... Fixed many function regexp's as per Peter J. Acklam's suggestion. 09Mar98 by Eric M. Ludlam Fixed `tempo-template-matlab-function' to work correctly. Fixed indentation for many other templates. Made sure the verifier uses navigation syntax. 23Feb98 by Eric M. Ludlam Fixed problem with x='%' % ' this shouldn't work Fixed a problem w/ strung up brackets messing up valid end identification. 17Feb98 by Aki Vehtari Fixed prompt regexp to include the debugging K. 11Feb98 by Eric M. Ludlam Made `matlab-mode-vf-functionname' more robust to arbitrary versions of a function definition. This includes allowing comments and blank lines before the first fn definition. Fixed up the font lock keywords for functions some 10Feb98 by Eric M. Ludlam Fixed problem with derived view mode. Fixed font locking of globals to allow a ; at the end. Fixed function name verifier to not allow = on next line. It used to match invalid expressions. `matlab-shell-collect-command-output' now uses a different prompt detector when waiting for output. This prevents early exit. 09Feb98 by Eric M. Ludlam Updated `matlab-indent-line' to not edit the buffer if no changes are needed, and to make after cursor position smarter. 05Feb98 by Eric M. Ludlam Added completion semantics and lists for HandleGraphics property lists Added `matlab-completion-technique' and made it's default value 'completion. This shows a buffer of completions instead of cycling through them as the hippie-expand command does. 26Jan98 by Aki Vehtari The Matlab logo variable now uses XEmacs 20.4 locate function. Small cleanups 26Jan98 by Eric M. Ludlam Updated `matlab-fill-paragraph' to use a better fill prefix. Moved code sections around, and added page breaks for navigation. 23Jan98 by Aki Vehtari (matlab-frame-init): Fix typo in menu. (matlab-output-to-temp-buffer): Use matlab-shell-help-mode. (matlab-shell-run-region): New function. (matlab-shell-collect-command-output): Remove (goto-char (point-max)). (matlab-shell-topic-mode-hook): Name change. (matlab-shell-topic-browser): Use matlab-shell-topic-mode. (matlab-shell-help-mode): New mode. Derive from view-major-mode. (matlab-shell-help-mode-menu): Define. (matlab-shell-topic-mode): Name change and derive from matlab-shell-help-mode. (matlab-shell-topic-mode-menu): Name change. 22Jan98 by Eric M. Ludlam Make `matlab-comment' insert `matlab-comment-s' on lines with no text when there there is no previous comment line to mimic. 21Jan98 by Eric M. Ludlam Fixed a few templates. Added `matlab-if-else'. `matlab-insert-end-block' will now add a comment consisting of the text starting the block being ended. Added colors to variables defined with the global command. Added `matlab-complete-symbol' which uses `matlab-find-recent-variable' which searches backwards for variables names, and `matlab-find-user-functions' which finds user functions. There are also `matlab-*-completions' for solo commands (if, else, etc), value commands, and boolean commands. The current semantic state is found w/ `matlab-lattr-semantics' 20Jan98 by Eric M. Ludlam Changed `matlab-block-scan-re' to have a limiting expression at the beginning. This makes sexp scanning faster by skipping more semantically bad matches. Forward/backward sexp now watch `matlab-scan-on-screen-only', which make them stop when the scan falls off the screen. Useful for making the block highlighter *much* faster for large constructs, and is logical since we can't see the highlight anyway. Added `matlab-block-verify-max-buffer-size' to turn off long checks on big buffers during save only. Requesting a verify will do the checks anyway. Fixed block verifiers to check that found end keywords are also valid block terminators. 19Jan98 by Eric M. Ludlam Fixed `gud-matlab-marker-filter' and `matlab-join-comment-lines' to not use `replace-match's fifth argument. Replaced `matlab-insert-' with tempo templates where appropriate. 19Jan98 by Aki Vehtari Fixed `matlab-mode-vf-functionname' to use a correct form of `replace-match' for XEmacs. Suggested form of `matlab-navigation-syntax'. 14Jan98 by Eric M. Ludlam Added manu `matlab-insert-' functions, including: `switch-block', `next-case', `end-block', `if-block', `for-block', `try-block', `while-block'. Added `matlab-stringify-region' which takes a region, and converts it to a string by adding ' around it, and quoting all the quotes in the region. Added an insertion prefix C-c C-c for all insert commands, and the stringify function. `matlab-auto-fill' is now assigned to `normal-auto-fill-function', which is an Emacs 20 thing for auto-fill minor mode. Added `matlab-beginning-of-command' and `end-of-command' which moves across lines w/ continuation. Changed `matlab-lattr-cont' to allow continuation on lines ending in semicolon. Is this correct? Changed the main menu to have submenues for navigation, formatting, and the new insert functions. Fixed `matlab-forward-sexp' to not skip over brackets which was appeared to be a missunderstanding. Block highlighter and block verifiers no longer treat function as requiring an "end" keyword. 09Jan98 by Eric M. Ludlam Based on code donated by Driscoll Tobin A `matlab-fill-paragraph' designed for M file help text, which will fill/justify comment text, and uses paragraph rules. `matlab-fill-comment-line' does not know about paragraphs. `matlab-cursor-in-string' can now take an optional argument which will identify an unterminated string. `matlab-auto-fill' will not fill strings, and if the string is not yet terminated, will also not fill it. When the string is terminated, the split will happen after the string, even if it occurs after the `fill-column'. 08Jan98 by Aki Vehtari XEmacs compatibility associated with timers. XEmacs optimizations associated with point-at-[eb]ol. Turned key sequences from strings to Emacs/XEmacs wide [()] form Documentation string fixes. Customizable hooks. Also update other custom vars. Remove `matlab-reset-vars' and turn variables controlled by `matlab-indent-function' into functions. Some menu re-arrangements & topic-browser menu. Use matlab-region-face instead of 'region when highlighting stuff. `matlab-shell-exit' now deletes the buffer when it's done. `write-contents-hooks' is forced buffer local. Fixed `matlab-output-to-temp-buffer'. Made matlab-shell group. 07Jan98 by Eric Ludlam Fixed indenting problem when end is first used as matrix index and is also the first word on a line. 07Jan98 by Aki Vehtari Fixed comments to use add-hook instead of setq. Variable name cleanup. Added ###autoload tags to -mode and -shell. Removed some unused variables. 24Dec97 by Eric Ludlam Added `matlab-shell-enable-gud-flag' to control if the GUD features are used in shell mode or not. This is automatically set to nil when certain GUD features are not present Added stop/clear if error to menu to help people out w/ the debugger. Added block highlighting of if/for/etc/end constructs. Fixed up cursor-in-string even more to handle bol better. Fixed problem w/ syntax table installing itself in funny places and fixed the fact that tab was now treated as whitespace. 22Dec97 by Eric Ludlam Added verify/fix mode when saving. Added function name check. Added unterminated block check. Added unmatched end check. Fixed `matlab-backward-sexp' to error on mismatched end/begin blocks. 15Dec97 by Eric Ludlam Fixed some string stuff, and added checks when starting the shell. 10Dec97 by Eric Ludlam Fixed string font-locking based on suggestions from: Hubert Selhofer Peter John Acklam Tim Toolan Fixed comment with ... indenting next line. Made command output collecting much faster. 10Dec97 merged the following: 21May97 by Alf-Ivar Holm Added smart initial values of matlab help commands. Running commands in matlab-shell remembers old cmd line Commands can be run when a parial command line is waiting Changed apropo to apropos where applicable. 9Dec98 merged the following: 30May97 by Hubert Selhofer Added 'endfunction' to keyword patterns (octave), slightly changed regexp for better performance. Added 'endfunction' to `matlab-block-end-pre-no-if' for compliance with octave. Fixed `matlab-clear-vars' (symbol names were incorrectly spelled matlab-matlab-*). Fixed typo in `matlab-really-gaudy-font-lock-keywords'. 26Nov97 by Eric Ludlam Added support for cell array indenting/continuation. Begin re-enumeration to V 2.0 11Nov97 by Eric Ludlam Added custom support for [X]emacs 20. 11Nov97 by Eric Ludlam Added beginning/end-defun navigation functions. Ran through latest version of checkdoc for good doc strings. 04Sep97 by Eric Ludlam Added try/catch blocks which are new Matlab 5.2 keywords. 02Sep97 by Eric Ludlam Made auto-fill mode more robust with regard to comments at the end of source lines 13Aug97 by Eric Ludlam Fixed indentation bugs regarding the beginning of buffer. Added GUD support into matlab-shell. Debugger commands will now automatically check the stack and post the files being examined via this facility. 26Jun97 by Eric Ludlam Help/Apropo buffers are now in Topic mode, and are highlighted. This allows navigation via key-clicks through the help. Describe-command can find a default in the current M file. Mouse-face set to make clickable items mouse-sensitive in topic buffers 25Jun97 by Anders Stenman Some XEmacs hacks. Implemented highlighting of subtopics and commands under mouse in topic-browser mode. Added a nice Matlab logo in matlab-shell mode. See: http://www.control.isy.liu.se/~stenman/matlab 13Jun97 by Anders Stenman Use the easymenu package for menus. Works both in XEmacs and FSF Emacs. Bound TAB to comint-dynamic-complete-filename in matlab-shell mode. Added a function matlab-shell-process-filter to filter out some escape character rubbish from Matlab output. 20May97 by Matt Wette Released as version 1.10.0. 16May97 by Eric Ludlam Ran through checkdoc to fix documentation strings. 15May97 by Matt Wette Added shell-mode-map bindings; run matlab-shell-mode-hook, not matlab-shell-mode-hooks (PMiller). Changed keymaps for \C-, which conflicted w/ emacs style guidelines. 08May97 by Eric Ludlam Fixed forward/backward sexp error when end keyword appears as word component such as the symbol the_end 22Apr97 by Eric Ludlam Fixed comment where `indent-function' was incorrectly spelled Fixed indentation when strings contained [] characters. Fixed indentation for multi-function files Added Imenu keywords. Permits use w/ imenu and emacs/speedbar The actual version of matlab file is not in a variable Keybinding for forward/backward sexp New function finds the mfile path. Not used for anything useful yet. Added matlab-shell/emacs io scripting functions. Used this in a topic/help/apropo browser. Could be used w/ other functions quite easily. 12Mar97 by Eric Ludlam Added new `matlab-shell-collect-command-output' to use for running matlab commands and getting strings back. Used this function to create `-describe-function', `-describe-variable', and `-apropo'. Should be useful for other things too. Added some XEmacs specific stuff. 07Mar97 by Matt Wette Fixed a few xemacs problems. Released as 1.09.0. 03Mar97 by Eric Ludlam Added expressions to handle blocks which are not terminated with the 'end' command Added `matlab-shell-save-and-go' function to automatically run a function after saving it. Bug fixes to `matlab-forward-sexp' Improved font lock interface to take advantage of the user variable `font-lock-use-maximal-decoration' 24Feb97 by Eric Ludlam Added more font locking, plus font locking of `matlab-shell' Added `matlab-backward-sexp',`matlab-cursor-in-string-or-comment' Added ability to indent switch/case/case/otherwise/end blocks as per manual specifications for matlab v5.0 Added command for matlab-shell to goto the last reported error Modified matlab-shell to use comint features instead of hand crafted workarounds of the defaults 07Dec96 by Matt Wette incorporated many fixes from Mats Bengtsson ; font-lock comment/string fixes, Eric Ludlam ; added support for switch construct; 01Aug96 by Matt Wette fixed to jive w/ emacs lib conventions: changed name of file from matlab-mode.el to matlab.el (14 char limit); released as 1.08.0 28Apr96 by Matt Wette comments lines w/ just % are now hilighted; syntax table: "-2" changed to " 2"; released 1.07.6 30Jan96 by Matt Wette fixed problem w/ emacs-19.30 filling and auto-fill problem thanks to Mats Bengtsson ; started implementation of matlab- shell, based on comint and shell-mode; released 1.07.5 25Jan96 by Matt Wette added "global" to font-lock, hilit keywords; fixed indenting of 2nd line if first ends in ...; filling is broken for FSF19.30 (works for FSF19.28); torkel fixes to matlab-reset-vars; fixed indent bug reported by Trevor Cooper; 20Jan96 by Matt Wette cleaned up commenting; added preliminary `matlab-shell' mode, rel 1.07.4 19Jan96 by Matt Wette commented out `debug-on-error'; got hilit to work for sam 18Jan96 by Matt Wette fixed problem int `matlab-prev-line' which caused fatal `matlab-mode'; crash fixed problem with indenting when keywords in comments; still haven't cleaned up comment formatting ... 21Jul95 by Matt Wette fixes by Bjorn Torkelsson : replaced lattr-comment w/ lattr-comm to fix inconsistency; added function to font-lock keywords, added function name to `font-lock-function-name-face'. He had also added function as a block begin keyword. This should be an option since it will cause the body of a function to be indented. Worked on filling. More work on filling. fixed many bugs reported by Rob Cunningham. Pulled cadr. 13Jul95 by Matt Wette changed indenting for continuation lines in calc-deltas to use cont-level; changed syntax-table; changed the way the return key is mapped; released version 1.07.1 08Jul95 by Matt Wette This is a fairly major rewrite of the indenting functions to fix long- startednding problems arising from keywords and percents in strings. We may have to add more heuristics later but this may work better. Changed comment region string. Released version 1.07.0. 10Oct94 by Matt Wette changed 'auto-fill-mode' to `auto-fill-function'; changed `comment-indent-' to `comment-indent-function'; fixed percents in strings being interpreted as comments, but a % for comment should not be followed by [disx%] 23Nov93 by Matt Wette added Lucid emacs, GNU emacs font-lock and lhilit support; repaired mtlb-block-{beg,end}-kw (Thanks to Dave Mellinger ) removed string delim entry from matlab-mode-syntax-table (MATLAB lang sucks here -- why not use " for strings?). Released vers 1.06.0 10Aug93 by Matt Wette added `matlab-indent-end-before-return'; indent may be fixed now still not working for emacs 19 02Aug93 by Matt Wette fixed error in `mtlb-calc-indent'; bumped version to 1.05.1; added `mtlb-prev-line'; bumped version to 1.05.3; added `mtlb-calc-block-indent' 01Aug93 by Matt Wette Fixed bug which treated form as block-begin keyword. Reworked `mtlb-calc-indent' -- seems to work better w/ redundant cont lines now. Bumbed version to 1.05. 13Jun93 by Matt Wette Changed `linea' to `lattr', `linet' to `ltype', fixed Bumped version number from 1.03bb to 1.04. 02May91 by Matt Wette, mwette@csi.jpl.nasa.gov Added `matlab-auto-fill' for `auto-fill-hook' so that this mode doesn't try to fill matlab code, just comments. 22Apr91 by Matt Wette, mwette@csi.jpl.nasa.gov Changed `mtlb-ltype-cont' to `mtlb-lattr-cont', `mtlb-ltype-comment-on-line' to `mtlb-lattr-comment' and `mtlb-ltype-unbal-mexp' to `mtlb- attr-unbal-mext' to emphasize that these are line attributes and not line types. Modified `matlab-line-type' to reflect the change ini logic. 18Apr91 by Matt Wette, mwette@csi.jpl.nasa.gov Modified `matlab-comment-return' so that when hit on a line with a comment at the end it will go to the comment column. To get the comment indented with the code, just hit TAB. 17Apr91 by Matt Wette, mwette@csi.jpl.nasa.gov Received critique from gray@scr.slb.com. Changed ml- to mtlb- due to possible conflict with mlsupport.el routines. Added `matlab-comment' -line-s and -on-line-s. Fixed bug in `matlab-comment' (set-fill-prefix). `matlab-comment-return' now works if called on a non-comment line. 04Mar91 by Matt Wette, mwette@csi.jpl.nasa.gov Added const `matlab-indent-before-return'. Released Version 1.02. 02Feb91 by Matt Wette, mwette@csi.jpl.nasa.gov Changed names of `ml-*-line' to `ml-ltype-*'. Cleaned up a lot. Added `ml-format-comment-line', fixed `ml-format-region'. Changed added "-s" on end of `matlab-comment-region' string. Justify needs to be cleaned up. Fri Feb 1 09:03:09 1991; gray@scr.slb.com Add function `matlab-comment-region', which inserts the string contained in the variable matlab-comment-region at the start of every line in the region. With an argument the region is uncommented. [Straight copy from fortran.el] 25Jan91 by Matt Wette, mwette@csi.jpl.nasa.gov Got indentation of matrix expression to work, I think. Also, added tabs to comment start regular-expression. 14Jan91 by Matt Wette, mwette@csi.jpl.nasa.gov Added functions `ml-unbal-matexp' `ml-matexp-indent' for matrix expressions. 07Jan91 by Matt Wette, mwette@csi.jpl.nasa.gov Many changes. Seems to work reasonably well. Still would like to add some support for filling in comments and handle continued matrix expressions. Released as Version 1.0. 04Jan91 by Matt Wette, mwette@csi.jpl.nasa.gov Created. Used eiffel.el as a guide. matlab-mode/matlab.el0000664000175000017500000041733514611713120015551 0ustar sebastiensebastien;;; matlab.el --- major mode for MATLAB(R) dot-m files ;; Author: Matt Wette , ;; Eric M. Ludlam ;; Maintainer: Eric M. Ludlam ;; Created: 04 Jan 91 ;; Keywords: MATLAB(R) ;; Version: (defconst matlab-mode-version "5.0" "Current version of MATLAB(R) mode.") ;; ;; Copyright (C) 1997-2022 Eric M. Ludlam ;; Copyright (C) 1991-1997 Matthew R. Wette ;; ;; 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, 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;; ;;; Commentary: ;; ;; This major mode for GNU Emacs provides support for editing MATLAB(R) dot-m ;; files. It automatically indents for block structures (including nested ;; functions), line continuations (e.g., ...), and comments. ;; ;; Additional features include auto-fill including auto-additions of ;; ellipsis for commands, and even strings. Block/end construct ;; highlighting as you edit. Primitive code-verification and ;; identification. Templates and other code editing functions. ;; Advanced symbol completion. Code highlighting via font-lock. ;; There are many navigation commands that let you move across blocks ;; of code at different levels. ;; ;; Lastly, there is support for running MATLAB(R) in an Emacs buffer, ;; with full shell history and debugger support (when used with the db ;; commands.) The shell can be used as an online help while editing ;; code, providing help on functions, variables, or running arbitrary ;; blocks of code from the buffer you are editing. ;;; Code: (require 'matlab-compat) (require 'matlab-syntax) (require 'matlab-scan) (require 'easymenu) (require 'derived) (eval-when-compile (require 'elec-pair)) ;;; User-changeable variables ================================================= ;; ;; Variables which the user can change (defgroup matlab nil "MATLAB(R) mode." :prefix "matlab-" :group 'languages) (defcustom matlab-mode-for-new-mfiles 'maybe "*Enter `matlab-mode' for new *.m files. The `matlab' package will automatically enter `matlab-mode' when the first part of a *.m file is doesn't contain Objective-C comments or '#' characters. If you want new (empty) files to automatically enter `matlab-mode', specify this item as t (always). If you specify 'maybe, new files will enter `matlab-mode' when you have an existing MATLAB buffer. Specifying nil (never) means that new *.m files will not enter `matlab-mode', and with default Emacs settings they will enter `objc-mode'" :group 'matlab :type '(choice (const :tag "Always" t) (const :tag "Never" nil) (const :tag "Maybe" maybe))) (defcustom matlab-indent-level 4 "*The basic indentation amount in `matlab-mode'." :group 'matlab :type 'integer) (defcustom matlab-continuation-indent-level 4 "*Basic indentation after continuation if no other methods are found." :group 'matlab :type 'integer) (defcustom matlab-array-continuation-indent-level 2 "*Basic indentation after continuation within an array if no other methods are found." :group 'matlab :type 'integer) (defcustom matlab-cont-requires-ellipsis t "*Specify if ellipses are required at the end of a line for continuation. Future versions of Matlab may not require ellipses ... , so a heuristic determining if there is to be continuation is used instead." :group 'matlab :type 'integer) (defcustom matlab-case-indent-level '(2 . 2) "*How far to indent case/otherwise statements in a switch. This can be an integer, which is the distance to indent the CASE and OTHERWISE commands, and how far to indent commands appearing in CASE and OTHERWISE blocks. It can also be a cons cell which is of form (CASEINDENT . COMMANDINDENT) where CASEINDENT is the indentation of the CASE and OTHERWISE statements, and COMMANDINDENT is the indentation of commands appearing after the CASE or OTHERWISE command. Note: Currently a bug exists if: CASEINDENT+COMMANDINDENT != `matlab-indent-level' so if you customize these variables, follow the above rule, and you should be ok." :group 'matlab :type 'sexp) (defcustom matlab-indent-past-arg1-functions "\\_<\\([sg]et\\(_param\\)?\\|waitfor\\|notify\\)\\_>" "*Regex describing functions whose first arg is special. This specialness means that all following parameters which appear on continued lines should appear indented to line up with the second argument, not the first argument." :group 'matlab :type 'string) (defcustom matlab-arg1-max-indent-length 15 "*The maximum length to indent when indenting past arg1. If arg1 is exceptionally long, then only this number of characters will be indented beyond the open paren starting the parameter list." :group 'matlab :type 'integer) (defcustom matlab-maximum-indents '(;; = is a convenience. Don't go too far (?= . (10 . 4)) ;; Fns should provide hard limits (?\( . 50) ;; Matrix/Cell arrays (?\[ . 20) (?\{ . 20)) "Alist of maximum indentations when lining up code. Each element is of the form (CHAR . INDENT) where char is a character the indent engine is using, and INDENT is the maximum indentation allowed. Indent could be of the form (MAXIMUM . INDENT), where MAXIMUM is the maximum allowed calculated indent, and INDENT is the amount to use if MAXIMUM is reached." :group 'matlab :type '(repeat (cons (character :tag "Open List Character") (sexp :tag "Number (max) or cons (max indent)")))) (defcustom matlab-align-to-paren t "*Whether continuation lines should be aligned to the opening parenthesis. When non-nil, continuation lines are aligned to the opening parenthesis if the opening is not followed by only spaces and ellipses. When nil, continued lines are simply indented by `matlab-continuation-indent-level'." :group 'matlab :type 'boolean ) (defcustom matlab-indent-function-body 'MathWorks-Standard "*If non-nil, indent body of function. If the global value is nil, do not indent function bodies. If the global value is t, always indent function bodies. If the global value is 'guess, then the local value will be set to either nil or t when the MATLAB mode is started in a buffer based on the file's current indentation. If the global value is 'MathWorks-Standard, then the local value is not changed, and functions are indented based on `matlab-functions-have-end'." :group 'matlab :type '(choice (const :tag "Always" t) (const :tag "Never" nil) (const :tag "Guess" guess) (const :tag "MathWorks Standard" MathWorks-Standard)) ) (make-variable-buffer-local 'matlab-indent-function-body) (put 'matlab-indent-function-body 'safe-local-variable #'symbolp) (defcustom matlab-functions-have-end 'guess "*If non-nil, functions-have-end minor mode is on by default. If the value is 'guess, then we guess if a file has end when `matlab-mode' is initialized." :group 'matlab :type 'boolean) (make-variable-buffer-local 'matlab-functions-have-end) (put 'matlab-functions-have-end 'safe-local-variable #'symbolp) (defun matlab-toggle-functions-have-end () "Toggle `matlab-functions-have-end-minor-mode'." (interactive) (matlab-toggle-functions-have-end-minor-mode)) ;; The following minor mode is on if and only if the above variable is true; (easy-mmode-define-minor-mode matlab-functions-have-end-minor-mode "Toggle functions-have-end minor mode, indicating function/end pairing." nil (:eval (cond ((eq matlab-functions-have-end 'guess) " function... ?") ((eq matlab-functions-have-end 'class) " classdef...end") (matlab-functions-have-end " function...end") (t " function..."))) nil ; empty mode-map ;; body of matlab-functions-have-end-minor-mode (let ((type (matlab-guess-script-type))) (if matlab-functions-have-end-minor-mode (if (eq type 'empty) (setq matlab-functions-have-end 'guess) (setq matlab-functions-have-end type)) (setq matlab-functions-have-end nil) ) ;; Depending on the kind of end, lets set other variables. (cond ((eq matlab-functions-have-end 'guess) ;;(setq matlab-syntax-support-command-dual t) ) ((eq matlab-functions-have-end 'class) ;;(setq matlab-syntax-support-command-dual nil) ) (matlab-functions-have-end ;;(setq matlab-syntax-support-command-dual t) ) (t ;;(setq matlab-syntax-support-command-dual nil) )) )) (defvar matlab-last-script-type-guess nil "The last time we guessed the script type, what was it?") (defun matlab-last-guess-decl-p () "Return non-nil if our last guess at a script type was function or class." (memq matlab-last-script-type-guess '(function class))) (defun matlab-guess-script-type () "Guess the type of script this `matlab-mode' file contains. Returns one of 'empty, 'script, 'function, 'class." (setq matlab-last-script-type-guess (cond ((not buffer-file-name) ;; Consider the case of exporting an org-mode '#+begin_src matlab' code block. In this case ;; org-mode will create a temporary buffer in matlab-mode, then insert the content. To ensure ;; keywords such as enumeration are syntactically recognized, we set the type to 'class which ;; covers all matlab language keywords. This works for both classdef's and non-classdef's. 'class) (t (save-excursion (goto-char (point-min)) (let ((lvl1 nil)) (cond ((not (matlab-find-code-line)) 'empty) ;; We found some code, what is it? ((and (setq lvl1 (matlab-compute-line-context 1)) (matlab-line-declaration-p lvl1)) ;; We are on a decl - distinguish between type (let ((str (matlab-line-first-word-text lvl1))) (cond ((string= str "function") 'function) ((string= str "classdef") 'class) (t (error "Error in script guessing algorithm."))))) (t ;; No function or class - just a script. 'script)))))))) (defun matlab-do-functions-have-end-p (&optional no-navigate) "Look at the contents of the current buffer and decide if functions have end. If the current value of `matlab-functions-have-end' is 'guess, look @ the buffer. If the value is t, then return that." (if (eq matlab-functions-have-end 'guess) ;; Lets guess what we think the answer is. (let ((type (matlab-guess-script-type))) (cond ((eq type 'empty) 'guess) ;; Keep guessing until we get some code. ((eq type 'script) 'script) ;; modern scripts can have functions, and they are required to have an end. ((eq type 'class) 'class) ;; classes always have ends. (no-navigate ;; Functions, but don't navigate ... stay in guess mode. 'guess) (t ;; functions but do navigate - we need to see if there is an end. (save-excursion (goto-char (point-min)) (matlab-find-code-line) (let ((matlab-functions-have-end t)) ;; pretend we have ends (back-to-indentation) (if (eq (matlab-on-keyword-p) 'decl) ;; If block scanning returns state, then that means ;; there is a missing end, so value is nil. ;; If it returns empty, then there is a matching end. (if (matlab--scan-block-forward) nil t) ;; Not on a decl, therefore just say nil, since block scanning would fail. nil) )))) ) ;; Else, just return the default. matlab-functions-have-end)) (defun matlab-toggle-functions-have-end-minor-mode () "Toggle `matlab-functions-have-end-minor-mode' only for `matlab-mode' buffers." (matlab-functions-have-end-minor-mode) (if (and matlab-functions-have-end-minor-mode (not (eq major-mode 'matlab-mode))) (progn (matlab-functions-have-end-minor-mode -1) (error "Mode `matlab-functions-have-end' minor mode is only for MATLAB Major mode"))) ) (defun matlab-indent-function-body-p () "Non-nil if functions bodies are indented. See `matlab-indent-function-body' variable." (if (eq matlab-indent-function-body 'MathWorks-Standard) ;; Dec '09 ;; The MathWorks standard is the same as if functions have end. matlab-functions-have-end ;; Else, just return the variable. matlab-indent-function-body)) (defun matlab-guess-function-indentation () "Look at the current buffer and determine if functions are indented. Setup various variables based on what we find." (let ((st (matlab-guess-script-type)) ) (cond ((not (eq st 'function)) ;; Anything not a function should follow the mathworks standard. (setq matlab-indent-function-body 'MathWorks-Standard) ) ;; If we are guessing, keep guessing (vaguely true) ((eq (matlab-do-functions-have-end-p t) 'guess) (setq matlab-indent-function-body 'guess)) ;; Here it is a function, and there are no ends. (t ;; Functions in guess mode we need to find the function decl ;; and then look at the first code line and see if it is indented ;; to guess what to do. (save-excursion (goto-char (point-min)) (matlab-find-code-line) ;; We are likely on the fcn line. Scan to end of it. (matlab-scan-end-of-command) ;; Now find next code line after comments (matlab-find-code-line) ;; If it is indented, then we too will indent. (setq matlab-indent-function-body (if (> (current-indentation) 0) (if (matlab-do-functions-have-end-p t) ;; if it indented and we have ends, that is std. 'MathWorks-Standard ;; no ends but indented, not the standard. t) (if (matlab-do-functions-have-end-p t) ;; have ends, not indented, force nil. nil ;; no ends and not indented, mw standard 'MathWorks-Standard))) ))) )) (defcustom matlab-fill-fudge 10 "Number of characters around `fill-column' we can fudge filling. Basically, there are places that are very convenient to fill at, but might not be the closest fill spot, or occur after `fill-column'. If they occur within this fudge factor, we will use them. Also, if none of the above occur, and we find a symbol to break at, but an open paren (group) starts or ends within this fudge factor, move there to boost the amount of fill leverage we can get." :group 'matlab :type 'integer) (defcustom matlab-fill-fudge-hard-maximum 79 "The longest line allowed when auto-filling code. This overcomes situations where the `fill-column' plus the `matlab-fill-fudge' is greater than some hard desired limit." :group 'matlab :type 'integer) (defcustom matlab-elipsis-string "..." "Text used to perform continuation on code lines. This is used to generate and identify continuation lines." :group 'matlab :type 'string) (defcustom matlab-fill-code nil "*If true, `auto-fill-mode' causes code lines to be automatically continued." :group 'matlab :type 'boolean) (defcustom matlab-fill-count-ellipsis-flag t "*Non-nil means to count the ellipsis when auto filling. This effectively shortens the `fill-column' by the length of `matlab-elipsis-string'." :group 'matlab :type 'boolean) (defcustom matlab-fill-strings-flag t "*Non-nil means that when auto-fill is on, strings are broken across lines. If `matlab-fill-count-ellipsis-flag' is non nil, this shortens the `fill-column' by the length of `matlab-elipsis-string'." :group 'matlab :type 'boolean) (defcustom matlab-comment-column 40 "*The goal comment column in `matlab-mode' buffers." :group 'matlab :type 'integer) (defcustom matlab-comment-anti-indent 0 "*Amount of anti-indentation to use for comments in relation to code." :group 'matlab :type 'integer) (defcustom matlab-comment-line-s "% " "*String to start comment on line by itself." :group 'matlab :type 'string) (defcustom matlab-comment-on-line-s "% " "*String to start comment on line with code." :group 'matlab :type 'string) (defcustom matlab-comment-region-s "% $$$ " "*String inserted by \\[matlab-comment-region] at start of each line in \ region." :group 'matlab :type 'string) (defcustom matlab-verify-on-save-flag t "*Non-nil means to verify M whenever we save a file." :group 'matlab :type 'boolean) (defcustom matlab-mode-verify-fix-functions '(matlab-mode-vf-functionname matlab-mode-vf-classname matlab-mode-vf-add-ends) "List of function symbols which perform a verification and fix to M code. Each function gets no arguments, and returns nothing. They can move point, but it will be restored for them." :group 'matlab :type '(repeat (choice :tag "Function: " '(matlab-mode-vf-functionname matlab-mode-vf-classname matlab-mode-vf-add-ends matlab-mode-vf-quiesce-buffer )))) (defcustom matlab-block-verify-max-buffer-size 50000 "*Largest buffer size allowed for block verification during save." :group 'matlab :type 'integer) (defcustom matlab-mode-hook nil "*List of functions to call on entry to MATLAB mode." :group 'matlab :type 'hook) (defcustom matlab-show-mlint-warnings nil "*If non-nil, show mlint warnings." :group 'matlab :type 'boolean) (make-variable-buffer-local 'matlab-show-mlint-warnings) (put 'matlab-show-mlint-warnings 'safe-local-variable #'booleanp) (defcustom matlab-highlight-cross-function-variables nil "*If non-nil, highlight cross-function variables." :group 'matlab :type 'boolean) (make-variable-buffer-local 'matlab-highlight-cross-function-variables) (put 'matlab-highlight-cross-function-variables 'safe-local-variable #'booleanp) (defcustom matlab-return-add-semicolon nil "*If non nil, check to see a semicolon is needed when RET is pressed." :group 'matlab :type 'boolean) (make-variable-buffer-local 'matlab-return-add-semicolon) (defcustom matlab-change-current-directory nil "*If non nil, make file's directory the current directory when evaluating it." :group 'matlab :type 'boolean) (make-variable-buffer-local 'matlab-change-current-directory) (defvar matlab-mode-abbrev-table nil "The abbrev table used in `matlab-mode' buffers.") (define-abbrev-table 'matlab-mode-abbrev-table ()) ;;; Keybindings =============================================================== (defvar matlab-help-map (let ((km (make-sparse-keymap))) (define-key km "r" 'matlab-shell-run-command) (define-key km "f" 'matlab-shell-describe-command) (define-key km "a" 'matlab-shell-apropos) (define-key km "v" 'matlab-shell-describe-variable) km) "The help key map for `matlab-mode' and `matlab-shell-mode'.") ;; mode map (defvar matlab-mode-map (let ((km (make-sparse-keymap))) ;; Navigation Commands (define-key km [(meta a)] 'matlab-beginning-of-command) (define-key km [(meta e)] 'matlab-end-of-command) ;; Insert, Fill stuff (define-key km [(control c) (control c)] 'matlab-insert-map-fcn) (define-key km [(control c) (control f)] 'matlab-fill-comment-line) (define-key km [(control c) (control j)] 'matlab-justify-line) (define-key km [(control c) (control q)] 'matlab-fill-region) ;; Comment Stuff (define-key km "%" 'matlab-electric-comment) (define-key km "^" 'matlab-electric-comment) (define-key km "}" 'matlab-electric-block-comment) (define-key km "{" 'matlab-electric-block-comment) (define-key km "\C-c;" 'matlab-comment-region) (define-key km "\C-c:" 'matlab-uncomment-region) (define-key km [(meta \;)] 'matlab-comment) (define-key km [(meta j)] 'matlab-comment-line-break-function) (define-key km [(control c) return] 'matlab-comment-return) (substitute-key-definition 'comment-region 'matlab-comment-region km global-map) ;torkel ;; Completion (define-key km "\M-\t" 'matlab-complete-symbol) ;; Connecting to MATLAB Shell (define-key km [(control c) (control s)] 'matlab-shell-save-and-go) (define-key km [(control c) (control r)] 'matlab-shell-run-region) (define-key km [(meta control return)] 'matlab-shell-run-cell) (define-key km [(control return)] 'matlab-shell-run-region-or-line) (define-key km [(control c) (control t)] 'matlab-show-line-info) (define-key km [(control c) ?. ] 'matlab-shell-locate-fcn) (define-key km [(control h) (control m)] matlab-help-map) (define-key km [(meta s)] 'matlab-show-matlab-shell-buffer) (define-key km [(control meta mouse-2)] 'matlab-find-file-click) ;; Debugger interconnect (substitute-key-definition 'read-only-mode 'matlab-toggle-read-only km global-map) km) "The keymap used in `matlab-mode'.") ;;; TODO - this menu was all about when emacs didn't always have windows (e18 ?) ;; turn this into a regular menu definition. (defvar matlab-mode-menu-keymap nil "Keymap used in MATLAB mode to provide a menu.") ;; make a menu keymap (easy-menu-define matlab-mode-menu matlab-mode-map "MATLAB menu" '("MATLAB" ["Start MATLAB" matlab-shell :active (not (matlab-shell-active-p)) :visible (not (matlab-shell-active-p)) ] ["Switch to MATLAB" matlab-shell :active (matlab-any-shell-active-p) :visible (matlab-any-shell-active-p)] ["Save and go" matlab-shell-save-and-go :active (matlab-any-shell-active-p) ] ["Run Region" matlab-shell-run-region :active (matlab-any-shell-active-p) ] ["Run Cell" matlab-shell-run-cell :active (matlab-any-shell-active-p) ] ["Version" matlab-show-version t] "----" ["Locate MATLAB function" matlab-shell-locate-fcn :active (matlab-shell-active-p) :help "Run 'which FCN' in matlab-shell, then open the file in Emacs"] ["Show M-Lint Warnings" matlab-toggle-show-mlint-warnings :active (and (locate-library "mlint") (fboundp 'mlint-minor-mode)) :style toggle :selected matlab-show-mlint-warnings ] ("Auto Fix" ["Verify/Fix source" matlab-mode-verify-fix-file t] ["Spell check strings and comments" matlab-ispell-strings-and-comments t] ["Quiesce source" matlab-mode-vf-quiesce-buffer t] ) ("Format" ["Justify Line" matlab-justify-line t] ["Fill Region" matlab-fill-region t] ["Fill Comment Paragraph" matlab-fill-paragraph (save-excursion (matlab-comment-on-line))] ["Join Comment" matlab-join-comment-lines (save-excursion (matlab-comment-on-line))] ["Comment Region" matlab-comment-region t] ["Uncomment Region" matlab-uncomment-region t]) ("Debug" ["Edit File (toggle read-only)" matlab-shell-gud-mode-edit :help "Exit MATLAB debug minor mode to edit without exiting MATLAB's K>> prompt." :visible gud-matlab-debug-active ] ["Add Breakpoint (ebstop in FILE at point)" mlgud-break :active (matlab-shell-active-p) :help "When MATLAB debugger is active, set break point at current M-file point"] ["Remove Breakpoint (ebclear in FILE at point)" mlgud-remove :active (matlab-shell-active-p) :help "When MATLAB debugger is active, remove break point in FILE at point." ] ["List Breakpoints (ebstatus)" mlgud-list-breakpoints :active (matlab-shell-active-p) :help "List active breakpoints."] ["Step (dbstep in)" mlgud-step :active gud-matlab-debug-active :help "When MATLAB debugger is active, step into line"] ["Next (dbstep)" mlgud-next :active gud-matlab-debug-active :help "When MATLAB debugger is active, step one line"] ["Finish function (dbstep out)" mlgud-finish :active gud-matlab-debug-active :help "When MATLAB debugger is active, run to end of function"] ["Continue (dbcont)" mlgud-cont :active gud-matlab-debug-active :help "When MATLAB debugger is active, run to next break point or finish"] ["Evaluate Expression" matlab-shell-gud-show-symbol-value :active (matlab-any-shell-active-p) :help "When MATLAB is active, show value of the symbol under point."] ["Show Stack" mlg-show-stack :active gud-matlab-debug-active :help "When MATLAB debugger is active, show the stack in a buffer."] ;;; Advertise these more if we can get them working w/ mlgud's frame show. ;;; ["Up Call Stack (dbup)" mlgud-up ;;; :active gud-matlab-debug-active ;;; :help "When MATLAB debugger is active and at break point, go up a frame"] ;;; ["Down Call Stack (dbdown)" mlgud-down ;;; :active gud-matlab-debug-active ;;; :help "When MATLAB debugger is active and at break point, go down a frame"] ["Quit debugging (dbquit)" mlgud-stop-subjob :active gud-matlab-debug-active :help "When MATLAB debugger is active, stop debugging"] ) ;; TODO - how to autoload these? Do we want this menu? ;; ("Insert" ;; ["Complete Symbol" matlab-complete-symbol t] ;; ["Comment" matlab-comment t] ;; ["if end" tempo-template-matlab-if t] ;; ["if else end" tempo-template-matlab-if-else t] ;; ["for end" tempo-template-matlab-for t] ;; ["switch otherwise end" tempo-template-matlab-switch t] ;; ["Next case" matlab-insert-next-case t] ;; ["try catch end" tempo-template-matlab-try t] ;; ["while end" tempo-template-matlab-while t] ;; ["End of block" matlab-insert-end-block t] ;; ["Function" tempo-template-matlab-function t] ;; ["Stringify Region" matlab-stringify-region t] ;; ) ("Customize" ["Indent Function Body" (setq matlab-indent-function-body (not (matlab-indent-function-body-p))) :style toggle :selected matlab-indent-function-body] ["Functions Have end" matlab-toggle-functions-have-end :style toggle :selected matlab-functions-have-end] ["Verify File on Save" (setq matlab-verify-on-save-flag (not matlab-verify-on-save-flag)) :style toggle :selected matlab-verify-on-save-flag] ["Auto Fill does Code" (setq matlab-fill-code (not matlab-fill-code)) :style toggle :selected matlab-fill-code ] ["Highlight Cross-Function Variables" matlab-toggle-highlight-cross-function-variables :active (locate-library "mlint") :style toggle :selected matlab-highlight-cross-function-variables ] ["Add Needed Semicolon on RET" (setq matlab-return-add-semicolon (not matlab-return-add-semicolon)) :style toggle :selected matlab-return-add-semicolon ] ["Customize" (customize-group 'matlab) (and (featurep 'custom) (fboundp 'custom-declare-variable)) ] ) "----" ["Run M Command" matlab-shell-run-command (matlab-shell-active-p)] ["Describe Command" matlab-shell-describe-command (matlab-shell-active-p)] ["Describe Variable" matlab-shell-describe-variable (matlab-shell-active-p)] ["Command Apropos" matlab-shell-apropos (matlab-shell-active-p)] )) (easy-menu-add matlab-mode-menu matlab-mode-map) ;;; Font Lock : Character Vectors, Strings and Comments ================================ ;; ;; Combine these, but do all the matching internally instead of using regexp ;; because it's just too complex for a regular expression. (defface matlab-region-face '((t :inherit region)) "*Face used to highlight a matlab region." :group 'matlab) (defvar matlab-unterminated-string-face 'matlab-unterminated-string-face "Self reference for unterminated string face.") (defvar matlab-commanddual-string-face 'matlab-commanddual-string-face "Self reference for command dual string face.") (defvar matlab-simulink-keyword-face 'matlab-simulink-keyword-face "Self reference for simulink keywords.") (defvar matlab-nested-function-keyword-face 'matlab-nested-function-keyword-face "Self reference for nested function/end keywords.") (defvar matlab-cross-function-variable-face 'matlab-cross-function-variable-face "Self reference for cross-function variables.") (defvar matlab-cellbreak-face 'matlab-cellbreak-face "Self reference for cellbreaks.") (defvar matlab-math-face 'matlab-math-face "Self reference for math.") (defface matlab-unterminated-string-face '((t :inherit font-lock-string-face :underline t)) "*Face used to highlight unterminated strings." :group 'matlab) (defface matlab-commanddual-string-face '((t :inherit font-lock-string-face :slant italic)) "*Face used to highlight command dual string equivalent." :group 'matlab) (defface matlab-simulink-keyword-face '((t :inherit font-lock-builtin-face :underline t)) "*Face used to highlight simulink specific functions." :group 'matlab) (defface matlab-nested-function-keyword-face '((t :inherit font-lock-keyword-face :slant italic)) "*Face to use for cross-function variables.") (defface matlab-cross-function-variable-face '((t :weight bold :slant italic)) "*Face to use for cross-function variables." :group 'matlab) (defface matlab-cellbreak-face '((t :inherit font-lock-comment-face :overline t :bold t)) "*Face to use for cellbreak %% lines.") (defface matlab-ignored-comment-face '((t :inherit font-lock-comment-face :slant italic)) "*Face to use for ignored comments. Ignored comments are lines that start with '% $$$' or '%^'.") (defface matlab-pragma-face '((t :inherit font-lock-comment-face :bold t)) "*Face to use for cellbreak %% lines.") (defface matlab-math-face '((t :inherit font-lock-constant-face :slant italic)) "*Face to use for cellbreak %% lines.") ;;; Font Lock MLINT data highlighting ;; TODO - THE BELOW LOOKS BROKEN TO ME. ;; - these are used in font lock, but are hand adding overlays ;; - and returning no matches - but the font lock keywords try to add ;; - a font. NEEDS FIX (defun matlab-font-lock-nested-function-keyword-match (limit) "Find next nested function/end keyword for font-lock. Argument LIMIT is the maximum distance to search." ;; Because of the way overlays are setup, the cursor will be sitting ;; on either a "function" or "end" keyword. (catch 'result (let ((pos (point)) overlays) (while (< pos limit) (setq overlays (matlab-overlays-at pos)) (while overlays (let ((overlay (car overlays))) (when (matlab-overlay-get overlay 'nested-function) (when (= pos (matlab-overlay-start overlay)) (goto-char pos) ;; The following line presumably returns true. (throw 'result (re-search-forward "function" (+ pos 8) t))) (let ((end-of-overlay (- (matlab-overlay-end overlay) 3))) (when (<= pos end-of-overlay) (goto-char end-of-overlay) (throw 'result (re-search-forward "end" (+ end-of-overlay 3) t)))))) (setq overlays (cdr overlays))) (setq pos (matlab-next-overlay-change pos))) nil ;; no matches, stop ))) (defun matlab-font-lock-cross-function-variables-match (limit) "Find next cross-function variable for font-lock. Argument LIMIT is the maximum distance to search." (catch 'result (let ((pos (point)) overlays variables) (while (< pos limit) (let ((overlays (matlab-overlays-at pos))) (while overlays (let ((overlay (car overlays))) (setq variables (matlab-overlay-get overlay 'cross-function-variables)) (if variables (progn (goto-char pos) (setq pos (min limit (matlab-overlay-end overlay))) (if (re-search-forward variables pos t) (progn (throw 'result t)))))) (setq overlays (cdr overlays)))) (setq pos (matlab-next-overlay-change pos))) nil ;; no matches, stop ))) (defcustom matlab-hg-primitives-list '(;; start with basic / primitive objects "figure" "axes" "line" "surface" "patch" "text" "light" "image" "imagesc" "rectangle" "animatedline" ;; core utilities "set" "get" "reset" "copyobj" "findobj" "cla" "clf" "shg" ;; popular helpers "axis" "hold" "title" "xlabel" "ylabel" "zlabel" "xlim" "ylim" "zlim" "rlim" "thetalim" "lighting" "shading" "material" ;; popular cartesian charts "plot" "plot3" "semilogx" "semilogy" "loglog" "scatter" "scatter3" "stackedplot" "area" "errorbar" "bubblechart" "bubblechart3" "swarmchart" "swarmchart3" "spy" "histogram" "histogram2" "wordcloud" "bubblecloud" "heatmap" "parallelplot" "bar" "barh" "bar3" "bar3h" "stem" "stairs" "quiver" "quiver3" "stem3" "contour" "contourf" "contour3" "contourslice" "fcontour" ;; 3D "surf" "surfc" "surfl" "ribbon" "pcolor" "mesh" "meshc" "meshz" "waterfall" ;; anim "comet" "comet3" ;; polar "polarplot" "polarscatter" "polarhistogram" "polarbubblechart" ;; geographic "goeplot" "geoscatter" "geobubble" "geodensity" ;; function plots "fplot" "fplot3" "fimplicit" "fsurf" "fimplicit3" ;; misc tools "legend" "colorbar" "tiledlayout" "nexttile" "subplot" "annotation" ;; Components "uicontrol" "uimenu" "uitoolbar" "uitoggletool" "uipushtool" "uicontext" "uicontextmenu" ;; misc dialogs "uisetfont" "uisetcolor" "uigetfile" "uiputfile") "List of handle graphics functions used in highlighting. Customizing this variable is only useful if `regexp-opt' is available." :group 'matlab :type '(repeat (string :tag "HG Keyword: "))) (defcustom matlab-debug-list '("dbstop" "dbclear" "dbcont" "dbdown" "dbmex" "dbstack" "dbstatus" "dbstep" "dbtype" "dbup" "dbquit") "List of debug commands used in highlighting. Customizing this variable is only useful if `regexp-opt' is available." :group 'matlab :type '(repeat (string :tag "Debug Keyword: "))) (defcustom matlab-simulink-keywords '("simulink" "get_param" "set_param" "simget" "simset" "sim" "new_system" "open_system" "close_system" "save_system" "find_system" "add_block" "delete_block" "replace_block" "add_line" "delete_line" "replace_line" "bdroot" "bdclose" ) ;; Missing this regex "\\(mld\\|ss\\)[A-Z]\\w+\\)" "List of keywords to highlight for simulink." :group 'matlab :type '(repeat (string :tag "Debug Keyword: "))) (defcustom matlab-constants-keyword-list '("eps" "pi" "flintmax" "inf" "Inf" "nan" "NaN" "ans" "i" "j" "NaT" "true" "false") "List of constants and special variables in MATLAB." :group 'matlab :type '(repeat (string :tag "Debug Keyword: "))) (defun matlab-font-lock-regexp-opt (keywordlist) "Create a font-lock usable KEYWORDLIST matching regular expression. Uses `regex-opt' if available. Otherwise creates a 'dumb' expression." (concat "\\_<\\(" (if (fboundp 'regexp-opt) (regexp-opt keywordlist) (mapconcat (lambda (s) s) keywordlist "\\|")) "\\)\\_>")) ;;; Font lock keyword handlers ;; (defun matlab-font-lock-basic-keyword-match (limit) "Font lock matcher for basic keywords. Fails to match when keywords show up as variables, etc." (matlab--scan-next-keyword 'fl-simple limit)) (defun matlab-font-lock-vardecl-keyword-match (limit) "Font lock matcher for mcos keywords. Fails to match when keywords show up as variables, etc." (matlab--scan-next-keyword 'vardecl limit)) (defvar matlab-fl-anchor-keyword nil) (defun matlab-font-lock-mcos-keyword-match (limit) "Font lock matcher for mcos keywords. Fails to match when keywords show up as variables, etc." (when (and (eq matlab-functions-have-end 'class) (setq matlab-fl-anchor-keyword (matlab--scan-next-keyword 'mcos limit))) (save-match-data ;; Skip over attributes. (when (looking-at "\\s-*(") (forward-sexp 1))) t)) (defun matlab-font-lock-args-keyword-match (limit) "Font lock matcher for mcos keywords. Fails to match when keywords show up as variables, etc." (setq matlab-fl-anchor-keyword (matlab--scan-next-keyword 'args limit))) (defvar font-lock-beg) (defvar font-lock-end) ; quiet compiler. (defun matlab-font-lock-extend-region () "Called by font-lock to extend the region for multiline expressions. Supports expressions like arguments and property blocks with anchored color support." (save-excursion (let* ((flb font-lock-beg) (fle font-lock-end) (tmp (matlab--scan-block-backward-up (window-start))) (blockmatch (when (not tmp) (matlab--mk-keyword-node)))) (when (and (member (nth 1 blockmatch) '("properties" "events" "arguments")) (matlab--valid-keyword-node blockmatch)) (setq font-lock-beg (min font-lock-beg (point-at-bol))) (when (not (matlab--scan-next-keyword 'all (window-end))) (setq font-lock-end (max font-lock-end (point-at-eol))))) (if (and (eq font-lock-beg flb) (eq font-lock-end fle)) ;; We didn't change anything. nil ;; We made a change t)))) (defvar ml-fl-anchor-limit nil) (defun matlab-font-lock-anchor-set-end-limit () "Set the end limit for anchored matchers." (save-excursion (save-match-data ;; next keyword is faster, plus if someone is in the middle of typing ;; a new block, prevents going too far into the distance. (matlab--scan-next-keyword 'all (point-max)) (forward-word -1) (setq ml-fl-anchor-limit (point))))) (defun matlab-font-lock-anchor-clear-end-limit () "Clear the end limit for anchored matchers." (setq ml-fl-anchor-limit nil)) (defun matlab-font-lock-anchor-variable-match (limit) "After finding a keyword like PROPERTIES or ARGUMENTS, match vars. This matcher will handle a range of variable features." (when (member (nth 1 matlab-fl-anchor-keyword) '("properties" "events" "arguments")) (let* ((match (re-search-forward "\\(?:^\\|[,;]\\)\\s-+\\(\\(?:\\w+\\|\\.\\)+\\)\\_>" ml-fl-anchor-limit t)) ;; Save this match so we can do a 2nd anchored search for a data type. (md1 (list (match-beginning 1) (match-end 1))) (tm (looking-at "\\(\\(?:\\s-*([^\n\)]+)\\s-*\\|\\s-+\\)?\\(?:\\w+\\(?:\\.\\w+\\)*\\)?\\)\\s-*\\($\\|[;%{=]\\)")) (tm1 (if tm (list (match-beginning 1) (match-end 1)) ;; The below is a cheat to not highlight anything but ;; still supply the match data for this optional piece. (list (nth 1 md1) (nth 1 md1)))) (newmdata (append md1 md1 tm1))) (when match (goto-char (point-at-eol)) (set-match-data newmdata) t)))) ;;; Font Lock keyword handling ;; ;; Many parts of the keyword handling are shared with matlab-shell. ;; The matlab based variables here are divided up between generic keywords ;; and keywords only for M files. This means the M shell won't highlight ;; some syntaxes like classdef stuff even though someone might paste them in. ;; ;; matlab-*-keywords -- MATLAB Files or Shell ;; matlab-file-*-keywords -- MATLAB Files only (defconst matlab-basic-font-lock-keywords (list ;; General keywords '(matlab-font-lock-basic-keyword-match (0 font-lock-keyword-face)) ;; Handle graphics stuff (list (matlab-font-lock-regexp-opt matlab-hg-primitives-list) '(0 font-lock-builtin-face)) (list ;; How about a few matlab constants such as pi, infinity, and sqrt(-1)? (matlab-font-lock-regexp-opt matlab-constants-keyword-list) 1 'matlab-math-face) ;; Imaginary number support '("\\<[0-9]\\.?\\(i\\|j\\)\\_>" 1 font-lock-constant-face) ) "Basic Expressions to highlight in MATLAB mode or shell.") (defconst matlab-file-basic-font-lock-keywords (append matlab-basic-font-lock-keywords '(;; MCOS keywords like properties, methods, events (matlab-font-lock-mcos-keyword-match (1 font-lock-keyword-face)) ;; ARGUMENTS keyword (matlab-font-lock-args-keyword-match (1 font-lock-keyword-face)) ;; Highlight cross function variables (matlab-font-lock-cross-function-variables-match (1 matlab-cross-function-variable-face prepend)) ;; Highlight nested function/end keywords (matlab-font-lock-nested-function-keyword-match (0 matlab-nested-function-keyword-face prepend)) )) "Basic Expressions to highlight in MATLAB Files.") (defconst matlab-fl-opt-continuation "\\s<\\S>+\\s>") (defconst matlab-fl-opt-whitespace (concat "\\s-*\\(?:" matlab-fl-opt-continuation "\\)?\\s-*")) (defconst matlab-fl-fcn-key "^\\s-*function\\_>") (defconst matlab-fl-return-args "\\(\\[[^]]*\\]\\|\\sw+\\)") (defconst matlab-fl-fcn-name "\\(?:[sg]et\\.\\)?\\sw+") (defconst matlab-fl-fcn-args "\\(?:(\\|$\\|\\s<\\)" ) (defconst matlab-function-font-lock-keywords (list ;; defining a function, a (possibly empty) list of assigned variables, ;; function name, and an optional (possibly empty) list of input variables (list (concat matlab-fl-fcn-key matlab-fl-opt-whitespace matlab-fl-return-args matlab-fl-opt-whitespace "=" matlab-fl-opt-whitespace "\\(" matlab-fl-fcn-name "\\)" matlab-fl-opt-whitespace matlab-fl-fcn-args) '(1 font-lock-variable-name-face append) '(2 font-lock-function-name-face prepend)) ;; defining a function, a function name, and an optional (possibly ;; empty) list of input variables (list (concat matlab-fl-fcn-key matlab-fl-opt-whitespace "\\(" matlab-fl-fcn-name "\\)" matlab-fl-opt-whitespace matlab-fl-fcn-args) '(1 font-lock-function-name-face prepend)) ;; Anchor on the function keyword, highlight params (list (concat matlab-fl-fcn-key matlab-fl-opt-whitespace "\\(" matlab-fl-return-args matlab-fl-opt-whitespace "=" matlab-fl-opt-whitespace "\\)?" matlab-fl-fcn-name matlab-fl-opt-whitespace "(") (list (concat matlab-fl-opt-whitespace "\\(\\sw+\\)" matlab-fl-opt-whitespace "[,)]") '(save-excursion (condition-case nil (matlab-scan-end-of-command) (error (point-at-eol)))) nil '(1 font-lock-variable-name-face))) ;; ARGUMENTS have variables to highlight '(matlab-font-lock-args-keyword-match (matlab-font-lock-anchor-variable-match ;; matcher fcn (matlab-font-lock-anchor-set-end-limit) ;; pre forms (matlab-font-lock-anchor-clear-end-limit) ;; post forms (1 font-lock-variable-name-face t) (2 font-lock-type-face t) )) ;; VARDECL keywords '(matlab-font-lock-vardecl-keyword-match ("\\(\\w+\\)\\(\\s-*=[^,; \t\n]+\\|[, \t;]+\\|$\\)" nil nil (1 font-lock-variable-name-face))) ;; I like variables for FOR loops '("\\<\\(\\(?:par\\)?for\\)\\s-+\\(\\sw+\\)\\s-*=\\s-*\ \\(\\([^\n,;%(]+\\|([^\n%)]+)\\)+\\)" (1 font-lock-keyword-face) (2 font-lock-variable-name-face append) (3 font-lock-constant-face append)) ;; Items after a switch statements are cool '("\\_<\\(case\\|switch\\)\\_>\\s-+\\({[^}\n]+}\\|[^,%\n]+\\)" (2 font-lock-constant-face)) ;; set_param and waitfor have input variables that can be highlighted. (list (concat matlab-indent-past-arg1-functions "\\s-*") '("(\\s-*\\(\\(?:\\w\\|\\.\\)+\\)\\s-*\\(,\\|)\\)" nil nil (1 font-lock-variable-name-face))) ) "List of font lock keywords for stuff in functions.") (defconst matlab-class-attributes-list-re "\\s-*\\(?2:(\\([^)]+\\))\\|\\)" "Regular expression for matching an attributes block.") (defconst matlab-file-class-font-lock-keywords (list ;; Classdefs keyword and the class name (list (concat "^\\s-*\\(classdef\\)\\_>" matlab-class-attributes-list-re "\\s-*\\(?3:\\sw+\\)") ;; '(1 font-lock-keyword-face append) - handled as keyword '(3 font-lock-function-name-face) ) ;; Classdef anchor for highlighting all the base classes in inherits from (list (concat "^\\s-*\\(classdef\\)" matlab-class-attributes-list-re "\\s-+\\(\\sw+\\)") '("\\s-*[<&]\\s-*\\(\\(\\sw\\|\\.\\)+\\)" nil nil (1 font-lock-constant-face))) ;; Property and Method blocks have attributes to highlight (list "^\\s-*\\(classdef\\|properties\\|methods\\|events\\|arguments\\)\\s-*(" '("\\(\\sw+\\)\\s-*\\(=\\s-*[^,)]+\\)?" nil nil (1 font-lock-type-face) )) ;; PROPERTY, EVENTS, etc have variables to highlight '(matlab-font-lock-mcos-keyword-match (matlab-font-lock-anchor-variable-match ;; matcher fcn (matlab-font-lock-anchor-set-end-limit) ;; pre forms (matlab-font-lock-anchor-clear-end-limit) ;; post forms (1 font-lock-variable-name-face t) (2 font-lock-type-face t) )) ) "List of font-lock keywords used when an MATLAB file contains a class.") (defconst matlab-file-gaudy-font-lock-keywords (append matlab-basic-font-lock-keywords matlab-file-basic-font-lock-keywords matlab-function-font-lock-keywords matlab-file-class-font-lock-keywords ) "Expressions to highlight in MATLAB mode.") (defconst matlab-really-gaudy-font-lock-keywords (append (list ;; Since it's a math language, how bout dem symbols? '("\\([<>~=]=\\|\\.[/\\*^'?]\\|\\_<\\(?:\\\\|[-<>!?^&|*+\\/~:@]\\)" 1 font-lock-builtin-face) ;; highlight transpose '("[]A-Za-z0-9_\"})']\\('+\\)" 1 font-lock-builtin-face) ;; How about references in the HELP text. (list (concat "^" matlab-comment-line-s "\\s-*" "\\(\\([A-Z]+\\s-*=\\s-+\\|\\[[^]]+]\\s-*=\\s-+\\|\\)" "\\([A-Z][0-9A-Z]+\\)\\(([^)\n]+)\\| \\)\\)") '(1 font-lock-constant-face prepend)) (list (concat "^" matlab-comment-line-s "\\s-*" "See also\\s-+") '("\\([A-Z][A-Z0-9]+\\)\\([,.]\\| and\\|$\\) *" nil nil (1 font-lock-constant-face prepend))) (list (concat "^" matlab-comment-line-s "\\s-*" "\\(\\$" "Revision" "[^\n$]+\\$\\)") '(1 font-lock-constant-face prepend)) ;; Debugging Keywords (list (matlab-font-lock-regexp-opt matlab-debug-list) '(0 'bold)) ;; Simulink functions (list (matlab-font-lock-regexp-opt matlab-simulink-keywords) 1 matlab-simulink-keyword-face) )) "Expressions to highlight in MATLAB mode.") (defconst matlab-file-really-gaudy-font-lock-keywords (append matlab-file-gaudy-font-lock-keywords matlab-really-gaudy-font-lock-keywords ) "Expressions to highlight in MATLAB mode.") ;; Imenu support. (defvar matlab-imenu-generic-expression '((nil "^\\s-*function\\>[ \t\n.]*\\(\\(\\[[^]]*\\]\\|\\sw+\\)[ \t\n.]*\ < =[ \t\n.]*\\)?\\([a-zA-Z0-9_]+\\)" 3)) "Expressions which find function headings in MATLAB M files.") ;;; MATLAB mode entry point ================================================== ;; Choose matlab-mode if when loading MATLAB *.m files ;; See "How Emacs Chooses a Major Mode" ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Auto-Major-Mode.html ;;;###autoload (defun matlab-is-matlab-file () "Enter `matlab-mode' when file content looks like a MATLAB *.m file or for empty files *.m files when `matlab-mode-for-new-mfiles' indicates as such." (and buffer-file-name ;; have a file? ;; AND a valid MATLAB file name (string-match "^\\(?:.*/\\)?[a-zA-Z][a-zA-Z0-9_]*\\.m\\'" ;; /path/to/file.m ? (file-name-sans-versions (if (and (boundp 'archive-subfile-mode) archive-subfile-mode) (aref archive-subfile-mode 0) ;; Will just be file.m without the directory buffer-file-name))) ;; AND (have MATLAB code OR an empty file that should enter matlab-mode) (or ;; Is content MATLAB code? We can definitely identify *some* MATLAB content using ;; (looking-at "^[[:space:]\n]*\\(%\\|function\\|classdef\\)") ;; i.e. '%', '%{' comments, or function/classdef start, but this fails to find MATLAB ;; scripts. Thus, if buffer is NOT Objective-C and has something in it, we assume MATLAB. ;; Objective-c is identified by ;; - comment start chars: // or /*, ;; - # char (as in #import) ;; - @ char (as in @interface) ;; MATLAB scripts are identified by the start of a valid identifier, i.e. a letter or ;; some math operation, e.g. [1,2,3]*[1,2,3]', thus all we really need to look for ;; is a non-whitespace character which could be a MATLAB comment, generic MATLAB commands, ;; function/classdef, etc. (and (not (looking-at "^[[:space:]\n]*\\(//\\|/\\*\\|#\\|@\\)")) (looking-at "^[[:space:]\n]*[^[:space:]\n]")) ;; Empty file - enter matlab-mode based on `matlab-mode-for-new-mfiles' setting (and (= (buffer-size) 0) (or (equal matlab-mode-for-new-mfiles t) (and (equal matlab-mode-for-new-mfiles 'maybe) ;; Enter matlab-mode if we already have a buffer in matlab-mode (let ((buffers (buffer-list)) enter-matlab-mode) (while buffers (with-current-buffer (car buffers) (when (or (eq major-mode 'matlab-mode) (eq major-mode 'matlab-shell-mode)) (setq enter-matlab-mode t) (setq buffers nil))) (setq buffers (cdr buffers))) enter-matlab-mode))))))) ;;;###autoload (add-to-list 'magic-mode-alist '(matlab-is-matlab-file . matlab-mode)) (defvar mlint-minor-mode) (declare-function mlint-minor-mode "mlint.el") (declare-function mlint-buffer "mlint.el") (declare-function mlint-clear-warnings "mlint.el") (declare-function mlint-clear-cross-function-variable-highlighting "mlint.el") (defvar show-paren-data-function) (defun matlab-mode-leave () "When leaving `matlab-mode', turn off `mlint-minor-mode'" (when (eq major-mode 'matlab-mode) (mlint-minor-mode -1) (matlab-scan-disable) )) ;;;###autoload (define-derived-mode matlab-mode prog-mode "MATLAB" "MATLAB(R) mode is a major mode for editing MATLAB dot-m files. \\ Convenient editing commands are: \\[matlab-comment-region] - Comment/Uncomment out a region of code. \\[matlab-fill-comment-line] - Fill the current comment line. \\[matlab-fill-region] - Fill code and comments in region. \\[matlab-complete-symbol] - Symbol completion of matlab symbols\ based on the local syntax. Convenient navigation commands are: \\[matlab-beginning-of-command] - Move to the beginning of a command. \\[matlab-end-of-command] - Move to the end of a command. Convenient template insertion commands: \\[tempo-template-matlab-function] - Insert a function definition. \\[tempo-template-matlab-if] - Insert an IF END block. \\[tempo-template-matlab-for] - Insert a FOR END block. \\[tempo-template-matlab-switch] - Insert a SWITCH END statement. \\[matlab-insert-next-case] - Insert the next CASE condition in a SWITCH. \\[matlab-insert-end-block] - Insert a matched END statement. With \ optional ARG, reindent. \\[matlab-stringify-region] - Convert plain text in region to a string \ with correctly quoted chars. Variables: `matlab-indent-level' Level to indent blocks. `matlab-continuation-indent-level' Level to indent after ... continuation `matlab-case-indent-level' Level to unindent case statements. `matlab-indent-past-arg1-functions' Regexp of functions to indent past the first argument on continuation lines. `matlab-maximum-indents' List of maximum indents during lineups. `matlab-comment-column' Goal column for on-line comments. `fill-column' Column used in auto-fill. `matlab-indent-function-body' If non-nil, indents body of MATLAB functions. `matlab-functions-have-end' If non-nil, MATLAB functions terminate with end. `matlab-fill-code' Non-nil, auto-fill code in auto-fill-mode. `matlab-fill-strings' Non-nil, auto-fill strings in auto-fill-mode. `matlab-verify-on-save-flag' Non-nil, enable code checks on save. `matlab-vers-on-startup' If t, show version on start-up. `matlab-handle-simulink' If t, enable simulink keyword highlighting. All Key Bindings: \\{matlab-mode-map}" :after-hook (matlab-mode-init-mlint-if-needed) (use-local-map matlab-mode-map) (setq major-mode 'matlab-mode) (setq mode-name "MATLAB") (if (boundp 'whitespace-modes) (add-to-list 'whitespace-modes 'matlab-mode)) (setq local-abbrev-table matlab-mode-abbrev-table) ;; Syntax tables and related features are in matlab-syntax.el ;; This includes syntax table definitions, misc syntax regexps ;; and font-lock for comments/strings. (matlab-syntax-setup) (matlab-scan-setup) ;; Indentation setup. (setq indent-tabs-mode nil) (make-local-variable 'indent-line-function) (setq indent-line-function 'matlab-indent-line) (make-local-variable 'indent-region-function) (setq indent-region-function 'matlab-indent-region) (make-local-variable 'comment-column) (setq comment-column matlab-comment-column) (make-local-variable 'comment-indent-function) (setq comment-indent-function (lambda () nil)) ;; always use indent-according-to-mode (make-local-variable 'electric-indent-functions) (setq electric-indent-functions 'matlab-electric-indent-function) ;; Sexp's and Defuns (make-local-variable 'forward-sexp-function) (setq forward-sexp-function 'matlab-forward-sexp-fcn) (make-local-variable 'beginning-of-defun-function) (setq beginning-of-defun-function 'matlab-beginning-of-defun) (make-local-variable 'end-of-defun-function) (setq end-of-defun-function 'matlab-skip-over-defun) (make-local-variable 'add-log-current-defun-function) (setq add-log-current-defun-function 'matlab-add-log-current-defun) ;; Auto-Fill and Friends (make-local-variable 'normal-auto-fill-function) (setq normal-auto-fill-function 'matlab-auto-fill) (make-local-variable 'fill-column) (setq fill-column matlab-fill-column) (make-local-variable 'fill-paragraph-function) (setq fill-paragraph-function 'matlab-fill-paragraph) (make-local-variable 'fill-prefix) (make-local-variable 'imenu-generic-expression) (setq imenu-generic-expression matlab-imenu-generic-expression) ;; Save hook for verifying src. This lets us change the name of ;; the function in `write-file' and have the change be saved. ;; It also lets us fix mistakes before a `save-and-go'. (make-local-variable 'write-contents-functions) (add-hook 'write-contents-functions 'matlab-mode-verify-fix-file-fn) ;; give each file it's own parameter history (make-local-variable 'matlab-shell-save-and-go-history) ;; Font lock support: (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((matlab-file-font-lock-keywords matlab-file-gaudy-font-lock-keywords matlab-file-really-gaudy-font-lock-keywords ) nil ; use syntax table comments/strings nil ; keywords are case sensitive. ;; This puts _ as a word constituent, ;; simplifying our keywords significantly ((?_ . "w")))) (setq font-lock-multiline 'undecided) (add-to-list 'font-lock-extend-region-functions #'matlab-font-lock-extend-region t) ;; Highlight parens OR if/end type blocks (make-local-variable 'show-paren-data-function) (setq show-paren-data-function 'matlab-show-paren-or-block) ;; Electric pair mode needs customization around transpose (make-local-variable 'electric-pair-inhibit-predicate) (setq electric-pair-inhibit-predicate 'matlab-electric-pair-inhibit-predicate) ;; Electric pair mode - handle ' as string delimiter correctly (make-local-variable 'electric-pair-pairs) (setq electric-pair-pairs '((39 . 39))) ;; If first function is terminated with an end statement, then functions have ;; ends. (if (matlab-do-functions-have-end-p) ;; minor mode now treat's 'guess' as true when passing in 1. (matlab-functions-have-end-minor-mode 1) (matlab-functions-have-end-minor-mode -1)) ;; When matlab-indent-function-body is set to 'MathWorks-Standard, ;; - we indent all functions that terminate with an end statement ;; - old style functions (those without end statements) are not ;; indented. ;; It is desired that all code be terminate with an end statement. ;; ;; When matlab-indent-function-body is set to 'guess, ;; - look at the first line of code and if indented, keep indentation ;; otherwise use MathWorks-Standard ;; (cond ((eq matlab-indent-function-body 'MathWorks-Standard) ) ((eq matlab-indent-function-body 'guess) (matlab-guess-function-indentation) ) ) ;; When leaving matlab-mode, turn off mlint (add-hook 'change-major-mode-hook #'matlab-mode-leave) ) (defun matlab-mode-init-mlint-if-needed () "Check if we should start `mlint-minor-mode' for this buffer." ;; Check to see if the user asked for any features that need mlint. (if (and (or (not (boundp 'mlint-minor-mode)) (not mlint-minor-mode)) ; prevent double init (or matlab-show-mlint-warnings matlab-highlight-cross-function-variables)) ; check settings for need ;; Some users may not feel like getting all the extra stuff ;; needed for mlint working. Do this only if we can get ;; mlint loaded ok. (condition-case nil (mlint-minor-mode (if (or matlab-show-mlint-warnings matlab-highlight-cross-function-variables) 1 0)) ;; If there is an error loading the stuff, don't ;; continue. (error nil)))) ;; Support debug mode and read only toggling. (defvar gud-matlab-debug-active nil) (declare-function matlab-shell-gud-minor-mode "matlab-shell-gud") (defun matlab-toggle-read-only (&optional arg interactive) "Toggle read-only bit in MATLAB mode. This looks to see if we are currently debugging, and if so re-enable our debugging feature. Optional argument ARG specifies if the read-only mode should be set. INTERACTIVE is ignored." (interactive "P") (if (and (featurep 'matlab-shell-gud) gud-matlab-debug-active) ;; The debugging is active, just re-enable debugging read-only-mode (matlab-shell-gud-minor-mode 1) ;; Else - it is not - probably doing something else. (call-interactively 'read-only-mode) )) ;;; Utilities ================================================================= (defun matlab-show-version () "Show the version number in the minibuffer." (interactive) (message "matlab-mode, version %s" matlab-mode-version)) (defun matlab-find-code-line () "Walk forwards until we are on a line of code return t on success. If the current line is code, return immediately. Ignore comments and whitespace." (forward-comment 100000) (not (eobp))) ;;; Navigation =============================================================== (defvar matlab-scan-on-screen-only nil "When this is set to non-nil, then forward/backward sexp stops off screen. This is so the block highlighter doesn't gobble up lots of time when a block is not terminated.") (defun matlab-backward-sexp (&optional autoend noerror) "Go backwards one balanced set of MATLAB expressions. If optional AUTOEND, then pretend we are at an end. If optional NOERROR, then we return t on success, and nil on failure. This assumes that expressions do not cross \"function\" at the left margin." (let ((p (point)) (returnme t) keyword) (save-excursion (skip-syntax-backward " .><") (cond ;; Auto end - Just go! (autoend (when (matlab--scan-block-backward-up nil) (if noerror (setq returnme nil) (error "Unstarted END"))) ) ;; No auto-end .... ;; End of a block comment ((eq (matlab-line-comment-style (matlab-compute-line-context 1)) 'block-end) (beginning-of-line) (matlab-beginning-of-string-or-comment)) ((or (not (setq keyword (matlab-on-keyword-p))) (memq keyword '(decl ctrl mcos arg vardecl keyword))) ;; Just walk over block starts and other random stuff. (matlab-move-simple-sexp-internal -1)) ((memq keyword '(mid case)) ;; If we're on a middle, then assume we're in the middle ;; of something and keep going. (when (matlab--scan-block-backward-up nil) (if noerror (setq returnme nil) (error "Unstarted END"))) ) (t (when (matlab--scan-block-backward nil) (if noerror (setq returnme nil) (error "Unstarted END"))) ) ) (when returnme (setq p (point)))) (goto-char p) returnme)) (defun matlab-forward-sexp-fcn (&optional arg) "Function used as `forward-sexp-function' for MATLAB mode. Adapt to use `matlab-forward-sexp' or `matlab-backward-sexp' depending on value of 'arg'." ;; Move forward on positive arg. (while (> arg 0) (matlab-forward-sexp) (setq arg (1- arg))) ;; Or maybe move backward on negative args. (while (< arg 0) (matlab-backward-sexp) (setq arg (1+ arg))) ) (defun matlab-forward-sexp (&optional autostart parentblock) "Go forward one balanced set of MATLAB expressions. If AUTOSTART is non-nil, assume we are already inside a block, and navigate forward until we exit that block. PARENTBLOCK is used when recursing to validate block starts as being in a valid context." (let (p keyword) ;; go to here if no error. (save-excursion ;; Don't move if there is an error ;; skip over preceding whitespace (skip-syntax-forward " .><") (cond ;; Auto start - just go! (autostart (when (matlab--scan-block-forward-up nil) (error "Unterminated Block") )) ;; No Autostart .... ;; Looking at a block comment. ((and (not autostart) (looking-at "%")) (goto-char (match-end 0)) (matlab-end-of-string-or-comment)) ((or (not (setq keyword (matlab-on-keyword-p))) (memq keyword '(end vardecl keyword))) ;; Just walk over ends and other random stuff. (matlab-move-simple-sexp-internal 1)) ((memq keyword '(mid case)) ;; If we're on a middle, then assume we're in the middle ;; of something and keep going. (when (matlab--scan-block-forward-up nil) (error "Unterminated Block")) ) (t (when (matlab--scan-block-forward nil nil) (error "Unterminated Block")) ) ) (setq p (point))) (goto-char p))) (defun matlab-beginning-of-defun (&optional arg) "Go to the beginning of the current function. With optional ARG, go backward that many defuns." (interactive "p") (unless arg (setq arg 1)) (let ((ans nil)) ;; If ARG is positive, move BACKWARD that many defuns. (while (> arg 0) (setq ans (matlab--beginning-of-defun-raw)) (setq arg (1- arg))) ;; If ARG is negative, move FORWARD that many defun (while (< arg 0) (if (eq (matlab-on-keyword-p) 'decl) (setq ans (not (matlab--scan-block-forward))) ;; Else, just look for stuff and hope for the best. (setq ans (matlab--scan-next-keyword 'decl (point-max))) ) (setq arg (1+ arg))) ans)) (defun matlab-skip-over-defun () "Assigned to `end-of-defun-function' for matlab mode. Assume point is on a defun, and if so, skip to the end." (skip-syntax-forward " >") (if (eq (matlab-on-keyword-p) 'decl) (matlab--scan-block-forward) ;; Else, bad condition. Maybe we're moving up from ;; inside a nested function? If so, bounce up ;; and try again. (matlab-end-of-defun 1))) (defun matlab-end-of-defun (&optional arg) "Go to the end of the current function." (interactive "p") (unless arg (setq arg 1)) (let ((ans nil)) (while (> arg 0) (matlab-end-of-string-or-comment t) (skip-syntax-forward " ") (when (not (eq (matlab-on-keyword-p) 'decl)) (matlab--scan-block-backward-up-until 'decl)) (skip-syntax-forward " ") (setq ans (if (eq (matlab-on-keyword-p) 'decl) (not (matlab--scan-block-forward)) nil)) (setq arg (1- arg))) ans)) (defun matlab--beginning-of-defun-raw () "Move to the beginning of defun cursor is in. Move up and backwards one defun, our out of current defun. Accounts for nested functions." ;; Get out of comments. (matlab-beginning-of-string-or-comment t) ;; back over whitespace - try to find what we are near. (skip-syntax-backward " >") ;; Do scanning (if (not (eq (matlab-on-keyword-p) 'end)) ;; No end, scan up until we find the declaration we're in. (matlab--scan-block-backward-up-until 'decl) ;; Else, nav backward over the end we are at. (matlab--scan-block-backward) (if (eq (matlab-on-keyword-p) 'decl) t ; done ;; If that end wasn't a decl, scan upward. (matlab--scan-block-backward-up-until 'decl)))) (defun matlab-add-log-current-defun () "Return a text string representing the current block. Tries to return the current defun. If not, look for a cell block with a name." (or (matlab-current-defun) (matlab-current-cell))) (defun matlab-current-cell () "Return the name of the current cell. The name is any text after the %% and any whitespace." (save-excursion (forward-page -1) (let ((lvl1 (matlab-compute-line-context 1)) start) (when (and (matlab-line-comment-p lvl1) (eq (matlab-line-comment-style lvl1) 'cell-start)) ;; We are in a cell start, get the content (goto-char (matlab-line-point lvl1)) (skip-chars-forward "% \t.,*" (point-at-eol)) (setq start (point)) (end-of-line 1) (skip-chars-backward " \t*" start) (buffer-substring-no-properties start (point)) )) )) (defun matlab-current-defun () "Return the name of the current function." (save-excursion (matlab--beginning-of-defun-raw) (nth 1 (matlab-line-declaration-name)))) (defun matlab-beginning-of-command () "Go to the beginning of an M command. Travels across continuations." (interactive "P") (matlab-scan-beginning-of-command)) (defun matlab-end-of-command () "Go to the end of an M command. Travels a cross continuations" (interactive) (matlab-scan-end-of-command)) ;;; Line types, attributes, and string/comment context ==================================== (defun matlab-line-continued-comment () "Return column of previous line's comment start, or nil." (save-excursion (beginning-of-line) (let* ((lvl (matlab-compute-line-context 1))) (if (or (null (matlab-line-regular-comment-p lvl)) (bobp)) nil ;; We use forward-line -1 and not matlab-previous-command-begin ;; because we want blank lines to terminate this indentation method. (forward-line -1) (matlab-line-end-comment-column (matlab-compute-line-context 1)))))) (defun matlab-line-count-block-change (&optional lvl1-start lvl1-end) "Count the change in block depth across lines. Start at LVL1-START, and end at LVL1-END. It is ok if these are the same line. A positive number means there were more block starts and ends. A negative number means there were more ends than starts. 0 means no blocks, or that all blocks started also ended." ;; TODO - delete this (unless lvl1-start (setq lvl1-start (matlab-compute-line-context 1) lvl1-end lvl1-start)) (if (or (matlab-line-comment-p lvl1-start) (matlab-line-empty-p lvl1-start)) ;; If we are starting on comments or empty, no code to scan 0 (matlab-with-context-line lvl1-start (let ((depth 0) (bounds (matlab-line-end-of-code lvl1-end)) (keyword nil)) ;; Lets scan for keywords. (goto-char (matlab-line-point lvl1-start)) (while (setq keyword (matlab--scan-next-keyword 'blocks bounds)) (cond ((eq (car keyword) 'end) (setq depth (1- depth))) (t (setq depth (1+ depth))))) depth)))) (defun matlab-function-called-at-point () "Return a string representing the function called nearby point." (save-excursion (beginning-of-line) (cond ((looking-at "\\s-*\\([a-zA-Z]\\w+\\)[^=][^=]") (match-string 1)) ((and (re-search-forward "=" (matlab-point-at-eol) t) (looking-at "\\s-*\\([a-zA-Z]\\w+\\)\\s-*[^=]")) (match-string 1)) (t nil)))) (defun matlab-comment-on-line () "Place the cursor on the beginning of a valid comment on this line. If there isn't one, then return nil, point otherwise." (interactive) (let* ((lvl1 (matlab-compute-line-context 1)) (comm (matlab-line-end-comment-point lvl1))) (if comm (goto-char comm) nil))) ;;; Indent functions ========================================================== ;; (defun matlab-indent-region (start end &optional column noprogress) "Indent the region between START And END for MATLAB mode. Unlike `indent-region-line-by-line', this function captures parsing state and re-uses that state along the way." (interactive) (save-excursion (setq end (copy-marker end)) (goto-char start) (let ((pr (when (and (not (minibufferp)) (not noprogress)) (make-progress-reporter "MATLAB Indenting region..." (point) end))) (lvl2 nil) (lvl1 nil) ) (while (< (point) end) (unless (and (bolp) (eolp)) ;; This is where we indent each line (setq lvl1 (matlab-compute-line-context 1) lvl2 (matlab-compute-line-context 2 lvl1)) ;; lvl2)) (when (matlab--indent-line lvl2) ;; If the indent changed something, refresh this ;; context obj. ;;(matlab-refresh-line-context-lvl2 lvl2) )) (forward-line 1) (and pr (progress-reporter-update pr (point)))) (and pr (progress-reporter-done pr)) (move-marker end nil)))) (defun matlab-indent-line () "Indent a line in `matlab-mode'." (interactive) (let ((lvl2 (matlab-compute-line-context 2))) (matlab--indent-line lvl2))) (defvar matlab--change-indentation-override #'matlab--change-indentation "Tests to override this to validate indent-region.") (defun matlab--indent-line (lvl2) "Indent the current line according to MATLAB mode. Input LVL2 is a pre-scanned context from `matlab-compute-line-context' lvl2. Used internally by `matlab-indent-line', and `matlab-indent-region'." (let* ((i (matlab--calc-indent lvl2))) (funcall matlab--change-indentation-override i))) (defun matlab--change-indentation (new-indentation) "Change the indentation on line to NEW-INDENTATION. This function exists so the test harness can override it." (let* ((i (max new-indentation 0)) (ci (current-indentation)) (cc (current-column)) (diff (- ci i))) (save-excursion (back-to-indentation) (cond ((= diff 0) ;; Already a match - do nothing. nil) ((< diff 0) ;; Too short - Add stuff (indent-to i)) ((<= diff ci) ;; Too much, delete some. (delete-region (save-excursion (move-to-column i t) (point)) (point)) ) (t ;; some sort of bug that wants to delete too much. Ignore. nil) )) (if (<= cc ci) (move-to-column (max 0 i))) (/= 0 diff))) (defun matlab--calc-indent (&optional lvl2 debug-sym) "Return the appropriate indentation for this line as an integer." ;; In case it wasn't provided. (unless lvl2 (setq lvl2 (matlab-compute-line-context 2))) ;; The first step is to find the current indentation. ;; This is defined to be zero if all previous lines are empty. (let* ((sem (matlab-calculate-indentation lvl2))) (when debug-sym (set debug-sym sem)) ;; simplistic (nth 1 sem))) (defun matlab--previous-line-indent-recommendation (lvl2) "Return the indentation recommendation from the previous line of CODE. Uses the lvl2 context of the current line of code it scans backward from. This function scans backward over blank lines and comments to find a line of code. It then scans that line and recommends either: - same indentation - if just boring old code. - indent more - if has control block openings on it." (cond ((save-excursion (beginning-of-line) (bobp)) ;; Beginning of buffer - do no work, just return 0. 0) ((matlab-line-close-paren-outer-point (matlab-get-lvl1-from-lvl2 lvl2)) ;; If we are inside an array continuation, then we shouldn't ;; need to do anything complicated here b/c we'll just ignore ;; the returned value in the next step. Return current indentation ;; of the previous non-empty line. (matlab-line-indentation (matlab-previous-nonempty-line lvl2))) (t ;; Else, the previous line might recommend an indentation based ;; on it's own context, like being a block open or continuation. (let ((prevcmd (matlab-previous-code-line lvl2))) (matlab-with-context-line prevcmd (cond ((and (matlab-line-empty-p prevcmd) (save-excursion (beginning-of-line) (bobp))) ;; Beginning of buffer - do no work, just return 0. 0) (t (matlab-next-line-indentation prevcmd)))))))) (defconst matlab-functions-have-end-should-be-true "This end closes a function definition.\nDo you want functions to have ends? " "Prompt the user about whether to change `matlab-functions-have-end'.") (defun matlab--maybe-yes-or-no-p (prompt noninteractive-default) "When in non-interactive mode run (yes-or-no-p prompt), otherwise return NONINTERACTIVE-DEFAULT" (if noninteractive noninteractive-default (yes-or-no-p prompt))) (defun matlab-calculate-indentation (&optional lvl2) "Calculate out the indentation of the current line. Return a list of descriptions for this line. Return format is: '(TYPE DEPTHNUMBER) where TYPE is one of (comment, code, function, blockstart, blockmid, blockendless, blockend) DEPTHNUMBER is how many characters to indent this line." (unless lvl2 (setq lvl2 (matlab-compute-line-context 2))) (let ((lvl1 (matlab-get-lvl1-from-lvl2 lvl2)) (tmp nil)) (cond ;; COMMENTS ((matlab-line-comment-p lvl1) (let ((comment-style (matlab-line-comment-style lvl1))) (cond ;; BLOCK END undoes body indent ((or (eq comment-style 'block-end) (eq comment-style 'block-body-prefix)) ; body prefix has same lineup rule (list 'comment (matlab-line-end-comment-column lvl1))) ;; BLOCK BODY is indented slightly from the start. ((eq comment-style 'block-body) (list 'comment (+ 2 (matlab-line-end-comment-column lvl1)))) ;; HELP COMMENT and COMMENT REGION ((and (matlab-last-guess-decl-p) (setq tmp (matlab-scan-comment-help-p lvl2))) (list 'comment-help tmp)) ;; BLOCK START is like regular comment ((eq comment-style 'block-start) ;; indent like code, but some users like anti-indent (list 'comment (+ (matlab--previous-line-indent-recommendation lvl2) matlab-comment-anti-indent)) ) ;; COMMENT REGION comments ((matlab-line-comment-ignore-p lvl1) (list 'comment-ignore 0)) ;; COMMENT Continued From Previous Line ((setq tmp (matlab-line-continued-comment)) ;;; TODO : REPLACE (list 'comment tmp)) (t (list 'comment (+ (matlab--previous-line-indent-recommendation lvl2) matlab-comment-anti-indent)))))) ;; FUNCTION DEFINITION ((matlab-line-declaration-p lvl1) (cond ((not matlab-functions-have-end) (setq tmp 0)) ;; If we do have ends, check if we are next ;; A function line has intrinsic indentation iff function bodies are ;; not indented and the function line is nested within another function. ((and (not (matlab-indent-function-body-p)) (save-excursion (beginning-of-line) ;; TODO - maybe replace this? Not usually used. (matlab--scan-block-backward-up-until 'decl))) (setq tmp (+ (matlab--previous-line-indent-recommendation lvl2) matlab-indent-level))) (t ;; If no intrinsic indentation, do not change from current-indentation. (setq tmp (matlab--previous-line-indent-recommendation lvl2))) ) (list 'function tmp)) ;; END keyword ((matlab-line-end-p lvl1) (let* ((CTXT (matlab-with-context-line lvl1 (matlab-scan-block-start-context)))) (if (eq (car CTXT) 'decl) ;; declarations (ie - function) is treated special. (if (or matlab-functions-have-end (if (matlab--maybe-yes-or-no-p matlab-functions-have-end-should-be-true t) (matlab-functions-have-end-minor-mode 1) (error "Unmatched end"))) (if (matlab-indent-function-body-p) ;; Match indentation of the function regardless of any other ;; state that might have gotten messed up. (setq tmp (matlab-line-indentation (nth 3 CTXT))) ;; Else, no change (setq tmp (matlab--previous-line-indent-recommendation lvl2)) )) ;; Not a declaration. In that case, just match up with the ;; line that the block stat is on. (setq tmp (matlab-line-indentation (nth 3 CTXT))))) (list 'blockend tmp)) ;; ELSE/CATCH keywords ((matlab-line-block-middle-p lvl1) (list 'blockmid (save-excursion (back-to-indentation) (if (matlab--scan-block-backward-up) (error "Missing start block") (if (not (eq (matlab-on-keyword-p) 'ctrl)) (error "Does not match opening block type")) (current-column))))) ;; CASE/OTHERWISE keywords ((matlab-line-block-case-p lvl1) (list 'blockendless (save-excursion (back-to-indentation) (if (matlab--scan-block-backward-up) (error "Missing switch") (if (not (eq (matlab-on-keyword-p) 'ctrl)) (error "Wrong type of start block") (+ (current-column) (if (listp matlab-case-indent-level) (car matlab-case-indent-level) matlab-case-indent-level))))))) ;; END of a MATRIX ((matlab-line-close-paren-p lvl1) (list 'array-end (let* ((fc (matlab-line-close-paren-inner-char lvl1)) (pc (matlab-line-close-paren-inner-col lvl1)) (mi (assoc fc matlab-maximum-indents)) (max (if mi (if (listp (cdr mi)) (car (cdr mi)) (cdr mi)) nil)) (ind (if mi (if (listp (cdr mi)) (cdr (cdr mi)) (cdr mi)) nil))) ;; apply the maximum limits. (if (and ind (> (- pc (matlab--previous-line-indent-recommendation lvl2)) max)) (1- ind) ; decor pc)))) ;; CODE LINES ((and (not (matlab-line-close-paren-outer-point lvl1)) (not (matlab-scan-previous-line-ellipsis-p))) ;; Code always matches up against the previous line. (list 'code (matlab--previous-line-indent-recommendation lvl2))) ;; CONTINUATION but from within a parenthetical: A group of cases for continuation ((matlab-line-close-paren-inner-col lvl1) (let* ((boc-lvl1 (save-excursion (matlab-scan-beginning-of-command) (matlab-compute-line-context 1))) (ci-boc (matlab-line-indentation boc-lvl1)) (boc (matlab-line-point boc-lvl1)) ;; Scratch vars for paren stuff (parencol (matlab-line-close-paren-inner-col lvl1)) (parenchar (matlab-line-close-paren-inner-char lvl1)) (parenpt (matlab-line-close-paren-inner-point lvl1)) (parenindent (when parenpt (save-excursion (goto-char parenpt) (current-indentation)))) (parenopt (matlab-line-close-paren-outer-point lvl1)) ;; What shall we use to describe this for debugging? (indent-type (cond ((and parenchar (= parenchar ?\()) 'function-call-cont) ((and parencol (= parenindent parencol)) 'array-solo-cont) ((and parenpt (/= parenpt parenopt)) 'nested-array-cont) (t 'code-cont))) ;; last not likely. (found-column nil) ) (save-excursion (cond ((and ;; CONTINUATION with FUNCTIONs that indent past arg1 (eq indent-type 'function-call-cont) ;; This checks for our special set of functions. (save-excursion (goto-char parenpt) (forward-symbol -1) (looking-at matlab-indent-past-arg1-functions)) ;; We are in a fcn call, AND the fcn wants to ;; indent past the first argument. Only do so ;; if first arg is a SIMPLE EXPR. (matlab-navigation-syntax (goto-char parenpt) (looking-at "(\\s-*\\(?:\\w\\|\\.\\)+\\s-*,") (setq found-column (match-end 0))) (save-excursion (goto-char found-column) ; move to comma ;; Don't bother if we hit the EOL. (not (looking-at "\\s-*\\(\\.\\.\\.\\|$\\|)\\)")))) ;; We are in the right kind of place. Lets ;; start indenting (goto-char found-column) (skip-chars-forward " \t") (if (> (- (current-column) parencol) matlab-arg1-max-indent-length) (setq tmp (+ parencol matlab-arg1-max-indent-length)) (setq tmp (current-column)))) ;; CONTINUATION with PARENS, BRACKETS, etc (t (let* ((mi (assoc parenchar matlab-maximum-indents)) (max (if mi (if (listp (cdr mi)) (car (cdr mi)) (cdr mi)) nil)) (ind (if mi (if (listp (cdr mi)) (cdr (cdr mi)) (cdr mi)) nil))) (goto-char parenpt) (forward-char 1) (skip-chars-forward " \t") ;; If we are at the end of a line and this ;; open paren is there, then we DON'T want ;; to indent to it. Use the standard ;; indent. (if (or (not matlab-align-to-paren) (looking-at "\\.\\.\\.\\|$")) (if (or (eq indent-type 'function-call-cont) (and (not (eq indent-type 'array-solo-cont)) (not (eq indent-type 'nested-array-cont)))) ;; functions or an array ending on a EOL should ;; do normal code indentation from beginning of cmd (setq tmp (+ ci-boc matlab-continuation-indent-level)) ;; If in an array in an array ending on EOL should ;; indent a wee bit (setq tmp (+ parencol matlab-array-continuation-indent-level))) ;; current column is location on original line where ;; first bit of text is, so line up with that. (setq tmp (current-column)) ;; TODO - this disables indentation MAXs ;; if we really want to be rid of this ;; we can dump a bunch of logic above too. ;; apply the maximum limits. ;;(if (and ind (> (- (current-column) ci-boc) max)) ;; (+ ci-boc ind) ;; (current-column)) ))) )) (list indent-type tmp))) (t ;; Other kinds of continuations (let* ((prev-lvl1 (save-excursion (forward-line -1) (matlab-compute-line-context 1))) (prev2-lvl1 (save-excursion (forward-line -2) (matlab-compute-line-context 1))) (ci-prev (matlab-line-indentation prev-lvl1)) (boc (matlab-line-point prev-lvl1)) (boc2 (matlab-line-point prev2-lvl1)) (indent-type (cond ((matlab-line-empty-p lvl1) 'empty) (t 'code-cont))) (found-column nil) ) (save-excursion (cond ;; Beginning of CONTINUATION has EQUALS ((and (or (not (matlab-line-ellipsis-p prev2-lvl1)) (= boc boc2)) (save-excursion (goto-char boc) (while (and (re-search-forward "=" (matlab-point-at-eol) t) (matlab-cursor-in-string-or-comment))) (when (= (preceding-char) ?=) (skip-chars-forward " \t") (setq found-column (point))) )) (save-excursion (goto-char found-column) (let ((cc (current-column)) (mi (assoc ?= matlab-maximum-indents)) (prev-indent (matlab--previous-line-indent-recommendation lvl2))) (if (looking-at "\\.\\.\\.\\|$") ;; In this case, the user obviously wants the ;; indentation to be somewhere else. (setq tmp (+ prev-indent (cdr (cdr mi)))) ;; If the indent delta is greater than the max, ;; use the max + current (if (and mi (> (- cc prev-indent) (if (listp (cdr mi)) (car (cdr mi)) (cdr mi)))) (setq tmp (+ prev-indent (if (listp (cdr mi)) (cdr (cdr mi)) (cdr mi)))) (setq tmp cc)))))) ;; CONTINUATION with nothing special about it. ((or (not (matlab-line-ellipsis-p prev2-lvl1)) (= boc boc2)) ;; Continued from non-continued line, push in just a little ;; Do explicit call to next-line-indentation b/c we've already computed the lvl1 context ;; on the beginning of command. (setq tmp (+ (matlab-next-line-indentation prev-lvl1) matlab-continuation-indent-level))) ;; CONTINUATION from a continued line, nothing special (t ;; Just match the same (setq tmp ci-prev)) )) (list indent-type tmp) )) ))) (defun matlab-next-line-indentation (lvl1) "Calculate the indentation for lines following this command line. See `matlab-calculate-indentation' for how the output of this fcn is used." (let ((startpnt (matlab-with-context-line lvl1 (point-at-eol))) ) (save-excursion (matlab-scan-beginning-of-command lvl1) (let* ((boc-lvl1 (matlab-compute-line-context 1)) (depthchange (matlab-line-count-block-change boc-lvl1 lvl1)) (end (matlab-line-end-p boc-lvl1)) ) ;; When DEPTHCHANGE is negative, and END is false, or ;; DEPTHCHANGE < -1, then the NEXT line should have an indent ;; that matches the context at the beginning of the block of ;; the last end. ;; ;; If we don't do this, and a block-start is not the FIRST item ;; on a line, then there is no way for the following line to figure ;; out where it should be. (if (or (< depthchange -1) (and (= depthchange -1) end)) (let* ((CTXT (matlab-with-context-line boc-lvl1 (matlab-line-end-of-code boc-lvl1) (matlab-re-search-keyword-backward (matlab-keyword-regex 'end) (point-at-bol) t) (matlab-scan-block-start-context)))) (matlab-line-indentation (nth 3 CTXT))) ;; Indent recommendations not related to ENDS ;; If we are NOT indenting our functions and we are on ;; a declaration, then we should subtract 1 from the beginning count. ;; This fairly simple change removes a big chunk of the old code. (when (and (not (matlab-indent-function-body-p)) (matlab-line-declaration-p boc-lvl1)) (setq depthchange (1- depthchange))) ;; Remove 1 from the close count if there is an END on the beginning ;; of this line, since in that case, the unindent has already happened. (when end (setq depthchange (1+ depthchange))) ;; Calculate the suggested indentation. (+ (current-indentation) (* matlab-indent-level depthchange) (* matlab-indent-level (if (matlab-line-block-middle-p boc-lvl1) 1 0)) (* (cdr matlab-case-indent-level) (if (matlab-line-block-case-p boc-lvl1) 1 0)) )))))) (defun matlab-electric-indent-function (char) "Return t if `electric-indent-mode' should indent after CHAR is inserted. Return nil otherwise. This function recommends indenting after special keywords that typically cause indentation changes so the code fixes itself up." (cond ((eq char ?e) (let ((lvl1 (matlab-compute-line-context 1))) (or (matlab-line-block-middle-p lvl1) (matlab-line-block-case-p lvl1)))) ((eq char ?d) (let ((lvl1 (matlab-compute-line-context 1))) (matlab-line-end-p lvl1))) (t nil))) ;;; Comment management======================================================== (defun matlab-comment-return () "Handle carriage return for MATLAB comment line." (interactive) (newline) (matlab-comment)) (defun matlab-electric-comment (arg) "Indent line and insert comment character. Argument ARG specifies how many %s to insert." (interactive "P") (self-insert-command (or arg 1)) (when (or (eq last-command-event ?%) (and (eq last-command-event ?^) ;; ignore comments move quick back to col0 (matlab-line-comment-p (matlab-compute-line-context 1)) (eq (char-before (1- (point))) ?%) )) (matlab-indent-line) (skip-chars-forward "%^"))) (defun matlab-electric-block-comment (arg) "Indent line and insert block comment end character. Argument ARG specifies how many %s to insert." (interactive "P") (self-insert-command (or arg 1)) (let* ((lvl1 (matlab-compute-line-context 1)) (bc (matlab-line-block-comment-start lvl1))) (cond ((eq (matlab-line-comment-style lvl1) 'block-start) (matlab-indent-line) ) ;;ELSE, maybe end of block comment ((eq (matlab-line-comment-style lvl1) 'block-end) (matlab-indent-line) (when bc (pulse-momentary-highlight-region bc (point))) ) ))) (defun matlab-comment () "Add a comment to the current line." (interactive) (let ((lvl1 (matlab-compute-line-context 1))) (cond ((region-active-p) (call-interactively #'comment-or-uncomment-region)) ((matlab-line-empty-p lvl1) ; empty line (insert matlab-comment-line-s) (back-to-indentation) (matlab-indent-line) (skip-chars-forward " \t%")) ((matlab-line-comment-p lvl1) ; comment line (back-to-indentation) (matlab-indent-line) (skip-chars-forward " \t%")) ((matlab-line-end-comment-column lvl1) ; code line w/ comment (goto-char (matlab-line-end-comment-point lvl1)) (if (> (current-column) comment-column) (delete-horizontal-space)) (if (< (current-column) comment-column) (indent-to comment-column)) ;; Now see if the current line is too long to fit. Can we back indent? (let ((eol-col (- (point-at-eol) (point-at-bol)))) (when (> eol-col fill-column) (delete-horizontal-space) (indent-to (- comment-column (- eol-col fill-column))))) (skip-chars-forward "% \t")) (t ; code line w/o comment (end-of-line) (re-search-backward "[^ \t\n^]" 0 t) (forward-char) (delete-horizontal-space) (if (< (current-column) comment-column) (indent-to comment-column) (insert " ")) (insert matlab-comment-on-line-s))))) (defun matlab-comment-line-break-function (&optional soft) "Break the current line, and if in a comment, continue it. Optional argument SOFT indicates that the newline is soft, and not hard." (interactive) (if (not (matlab-cursor-in-comment)) (progn (newline);;(matlab-return) (matlab-indent-line)) ;; Will the below fn work in old emacsen? (if soft (insert-and-inherit ?\n) (newline 1)) (insert "% ") (matlab-indent-line) (back-to-indentation) (skip-chars-forward "% "))) (defun matlab-comment-region (beg-region end-region arg) "Comments every line in the region. Puts `matlab-comment-region-s' at the beginning of every line in the region. BEG-REGION and END-REGION are arguments which specify the region boundaries. With non-nil ARG, uncomment the region." (interactive "*r\nP") (let ((end-region-mark (make-marker)) (save-point (point-marker))) (set-marker end-region-mark end-region) (goto-char beg-region) (beginning-of-line) (if (not arg) ;comment the region (progn (insert matlab-comment-region-s) (while (and (= (forward-line 1) 0) (< (point) end-region-mark)) (insert matlab-comment-region-s))) (let ((com (regexp-quote matlab-comment-region-s))) ;uncomment the region (if (looking-at com) (delete-region (point) (match-end 0))) (while (and (= (forward-line 1) 0) (< (point) end-region-mark)) (if (looking-at com) (delete-region (point) (match-end 0)))))) (goto-char save-point) (set-marker end-region-mark nil) (set-marker save-point nil))) (defun matlab-uncomment-region (beg end) "Uncomment the current region if it is commented out. Argument BEG and END indicate the region to uncomment." (interactive "*r") (matlab-comment-region beg end t)) ;;; Filling =================================================================== (defun matlab-set-comm-fill-prefix () "Set the `fill-prefix' for the current (comment) line." (interactive) (if (matlab-line-comment-p (matlab-compute-line-context 1)) (setq fill-prefix (save-excursion (beginning-of-line) (let ((e (matlab-point-at-eol)) (pf nil)) (while (and (re-search-forward "%+[ \t]*\\($$$ \\|\\* \\)?" e t) (matlab-cursor-in-string))) (setq pf (match-string 0)) (when (string-match "%\\s-*\\* " pf) (setq pf (concat "%" (make-string (1- (length pf)) ? )))) (concat (make-string (- (current-column) (length pf)) ? ) pf)))))) (defun matlab-set-comm-fill-prefix-post-code () "Set the `fill-prefix' for the current post-code comment line." (interactive) (matlab-set-comm-fill-prefix)) (defun matlab-reset-fill-prefix () "Reset the `fill-prefix'." (setq fill-prefix nil)) (defun matlab-find-convenient-line-break () "For the current line, position the cursor where we want to break the line. Basically, spaces are best, then operators. Always less than `fill-column' unless we decide we can fudge the numbers. Return nil if this line should not be broken. This function will ONLY work on code." ;; First of all, if this is a continuation, then the user is ;; requesting that we don't mess with his stuff. (let ((lvl1 (matlab-compute-line-context 1))) (if (matlab-line-ellipsis-p lvl1) nil (save-restriction (narrow-to-region (matlab-point-at-bol) (matlab-point-at-eol)) ;; get ourselves onto the fill-column. (move-to-column fill-column) (let ((pos nil) (orig (point))) (or ;; Next, if we have a trailing comment, use that. (progn (setq pos (or (matlab-line-comment-p lvl1) (matlab-point-at-bol))) (goto-char pos) (if (and (> (current-column) (- fill-column matlab-fill-fudge)) (< (current-column) (+ fill-column matlab-fill-fudge))) t (goto-char orig) nil)) ;; Now, lets find the nearest space (after or before fill column) (let* ((after (save-excursion (re-search-forward "[ \t]" nil t))) (before (save-excursion (re-search-backward "[ \t]" nil t))) (afterd (- (or after (matlab-point-at-eol)) (point))) (befored (- (point) (or before (matlab-point-at-bol))))) ;; Here, if "before" is actually the beginning of our ;; indentation, then this is most obviously a bad place to ;; break our lines. (if before (save-excursion (goto-char before) (if (<= (point) (save-excursion (back-to-indentation) (point))) (setq before nil)))) (cond ((and after (< afterd matlab-fill-fudge) (< afterd befored)) (goto-char after) t) ((and before (< befored matlab-fill-fudge) (< befored afterd)) (goto-char before) t) (t (goto-char orig) nil))) ;; Now, lets find the nearest backwards (progn (re-search-backward "\\(\\s-\\|\\s.\\)+" nil t) (while (and (looking-at "\\^\\|\\.\\|'") (re-search-backward "\\(\\s-\\|\\s.\\)+" nil t))) (if (or (not (looking-at "\\(\\s-\\|\\s.\\)+")) (<= (point) (save-excursion (back-to-indentation) (point)))) (progn ;; We failed in our mission to find anything, or fell ;; of the edge of the earth. If we are out of ;; bounds, lets try again. (goto-char orig) (if (re-search-backward "\\s.+" nil t) t nil)) ;; Ok, we have a good location to break. Check for column ;; and ref against nearest list ending to predict a possibly ;; better break point. (forward-char 1) (let ((okpos (current-column)) (startlst (save-excursion (condition-case nil (matlab-up-list -1) (error nil)) (if (save-excursion (forward-char -1) (looking-at "\\w")) (forward-word -1)) (current-column))) (endlst (save-excursion (condition-case nil (matlab-up-list 1) (error nil)) (current-column)))) ;; When evaluating list fudge factors, breaking on the ;; edge of a list, or at the beginning of a function ;; call can be more valuable than breaking on a symbol ;; of a mid-sized list. As such, allow double-fudge ;; for lists. (cond ;; First, pick the end of a list. ((and (< endlst matlab-fill-fudge-hard-maximum) (<= endlst (+ fill-column matlab-fill-fudge)) (or (<= (* matlab-fill-fudge 2) (- endlst okpos)) (<= endlst fill-column)) (save-excursion (move-to-column endlst) (not (looking-at "\\^")))) (move-to-column endlst) t) ;; Else, back up over this list and poke around ((>= (* 2 matlab-fill-fudge) (- okpos startlst)) (move-to-column startlst) t) ;; Oh well, just do this symbol. (t (move-to-column okpos) t))))) ;; Well, this just sucks (progn (goto-char orig) nil))))))) (defun matlab-auto-fill () "Do auto filling. Set variable `auto-fill-function' to this symbol to enable MATLAB style auto filling which will automatically insert `...' and the end of a line." (interactive) (let ((fill-prefix fill-prefix) ;; safe way of modifying fill-prefix. (fill-column (- fill-column (if matlab-fill-count-ellipsis-flag (save-excursion (move-to-column fill-column) (if (not (bobp)) (forward-char -1)) (if (matlab-cursor-in-string) 4 3)) 0))) (lvl1 (matlab-compute-line-context 1))) (if (> (current-column) fill-column) (cond ((matlab-line-comment-ignore-p lvl1) nil) ((or (matlab-line-comment-p lvl1) (and (save-excursion (move-to-column fill-column) (matlab-cursor-in-comment)) (matlab-line-comment-p lvl1))) ;; If the whole line is a comment, do this. (matlab-set-comm-fill-prefix) (do-auto-fill) (matlab-reset-fill-prefix)) ((and (not (or (matlab-line-comment-p lvl1) (matlab-line-empty-p lvl1))) (not (matlab-line-ellipsis-p lvl1)) matlab-fill-code) ;; If we are on a code line, we ellipsify before we fill. (let ((m (make-marker))) (move-marker m (point)) (set-marker-insertion-type m t) (if (not (matlab-find-convenient-line-break)) nil (if (not (save-excursion (forward-char -1) (matlab-cursor-in-string))) (progn (delete-horizontal-space) (insert " " matlab-elipsis-string "\n") (matlab-indent-line)) (if matlab-fill-strings-flag (let ((pos (point)) (pos2 nil)) (while (and (re-search-backward "'" (point-at-bol) t) (progn (forward-char -1) (looking-at "''")))) (setq pos2 (point)) ;; Check if there is already an opening bracket or if string is continued (if (or (looking-at "\\[") (save-excursion (skip-chars-backward " \t") (forward-char -1) (looking-at "\\[")) (progn (beginning-of-line) (skip-chars-backward (concat " \t\n" matlab-elipsis-string)) (if (> (point) (point-min)) (progn (forward-char -1) (looking-at (concat "'\\s-*" matlab-elipsis-string)))))) (goto-char pos) (goto-char pos2) (forward-char 1) (insert "[") (goto-char pos) (forward-char 1)) ;(delete-horizontal-space) (skip-chars-forward " \t") (insert "' " matlab-elipsis-string "\n") (matlab-indent-line) (insert "'") ;; Re scan forward for the end of the string. Add an end bracket ;; if there isn't one already. Also add an apostrophe if necessary. (if (not (looking-at "'\\s-*]")) (save-excursion (if (not (re-search-forward "[^']'\\([^']\\|$\\)" (line-end-position) t)) (progn (end-of-line) (insert "']") (move-marker m (- (point) 2))) (re-search-backward "'") (cond ((looking-at "'\\s-*]") nil ; already in an array. ) ((or (looking-at "'\\s-*$") (looking-at "'\\s-*[^]]")) ;; in a string, add an array end. (forward-char 1) (insert "]")) ((looking-at "'\\s-*\\.\\.\\.") ;; Already extended to next line ... leave it alone. nil) )))) )))) (goto-char m))) )))) (defun matlab-join-comment-lines () "Join current comment line to the next comment line." ;; New w/ V2.0: This used to join the previous line, but I could find ;; no editors that had a "join" that did that. I modified join to have ;; a behaviour I thought more inline with other editors. (interactive) (end-of-line) (if (looking-at "\n[ \t]*%") (replace-match " " t t nil) (error "No following comment to join with"))) (defun matlab-fill-region (beg-region end-region &optional justify-flag) "Fill the region between BEG-REGION and END-REGION. Non-nil JUSTIFY-FLAG means justify comment lines as well." (interactive "*r\nP") (let ((end-reg-mk (make-marker))) (set-marker end-reg-mk end-region) (goto-char beg-region) (beginning-of-line) (while (< (point) end-reg-mk) ;; This function must also leave the point at the end of the ;; justified line. (matlab-fill-paragraph justify-flag) (forward-line 1) (beginning-of-line)))) (defconst matlab-cline-start-skip "[ \t]*%[ \t]*" "*The regular expression for skipping comment start.") (defun matlab-fill-comment-line (&optional justify) "Fill the current comment line. With optional argument, JUSTIFY the comment as well." (interactive) (if (not (matlab-comment-on-line)) (error "No comment to fill")) (beginning-of-line) ;; First, find the beginning of this comment... (while (and (looking-at matlab-cline-start-skip) (not (bobp))) (forward-line -1) (beginning-of-line)) (if (not (looking-at matlab-cline-start-skip)) (forward-line 1)) ;; Now scan to the end of this comment so we have our outer bounds, ;; and narrow to that region. (save-restriction (narrow-to-region (point) (save-excursion (while (and (looking-at matlab-cline-start-skip) (not (save-excursion (end-of-line) (eobp)))) (forward-line 1) (beginning-of-line)) (if (not (looking-at matlab-cline-start-skip)) (forward-line -1)) (end-of-line) (point))) ;; Find the fill prefix... (matlab-comment-on-line) (looking-at "%[ \t]*") (let ((fill-prefix (concat (make-string (current-column) ? ) (match-string 0)))) (fill-region (point-min) (point-max) justify)))) (defun matlab-justify-line () "Delete space on end of line and justify." (interactive) (save-excursion (end-of-line) (delete-horizontal-space) (justify-current-line))) (defun matlab-fill-paragraph (arg) "When in a comment, fill the current paragraph. Paragraphs are always assumed to be in a comment. ARG is passed to `fill-paragraph' and will justify the text." (interactive "P") (cond ((or (matlab-line-comment-p (matlab-compute-line-context 1)) (and (matlab-cursor-in-comment) (not (matlab-line-ellipsis-p (matlab-compute-line-context 1))))) ;; We are in a comment, lets fill the paragraph with some ;; nice regular expressions. ;; Cell start/end markers of %% also separate paragraphs (let ((paragraph-separate "%%\\|%[a-zA-Z]\\|%[ \t]*$\\|[ \t]*$") (paragraph-start "%[a-zA-Z]\\|%[ \t]*$\\|[ \t]*$\\|%\\s-*\\*") (paragraph-ignore-fill-prefix nil) (start (save-excursion (matlab-scan-beginning-of-command) (if (looking-at "%%") (progn (end-of-line) (forward-char 1))) (point-at-bol))) (end (save-excursion (matlab-scan-end-of-command))) (fill-prefix nil)) (matlab-set-comm-fill-prefix) (save-restriction ;; Ben North fixed to handle comment at the end of ;; a buffer. (narrow-to-region start (min (point-max) (+ end 1))) (fill-paragraph arg)))) ((let ((lvl (matlab-compute-line-context 1))) (not (or (matlab-line-comment-p lvl) (matlab-line-empty-p lvl)))) ;; Ok, lets get the outer bounds of this command, then ;; completely refill it using the smart line breaking code. (save-restriction (narrow-to-region (save-excursion (matlab-scan-beginning-of-command) (beginning-of-line) (point)) (save-excursion (matlab-scan-end-of-command))) ;; Remove all line breaks (goto-char (point-min)) (while (and (re-search-forward "$" nil t) (not (eobp))) (delete-horizontal-space) ;; Blow away continuation marks (if (matlab-line-ellipsis-p (matlab-compute-line-context 1)) (progn (goto-char (match-beginning 0)) (forward-char 1) (delete-region (point) (matlab-point-at-eol)))) ;; Zap the CR (if (not (eobp)) (delete-char 1)) ;; Clean up whitespace (delete-horizontal-space) ;; Clean up trailing comments (if (and (looking-at "% *") (matlab-cursor-in-comment)) (progn (delete-char 1) (delete-horizontal-space))) (insert " ")) ;; Now fill till we are done (goto-char (point-max)) (while (or (> (current-column) (+ fill-column matlab-fill-fudge)) (> (current-column) matlab-fill-fudge-hard-maximum)) (if (= (point) (progn (matlab-auto-fill) (point))) (error "Fill algorithm failed!")) (if arg (save-excursion (forward-line -1) (matlab-justify-line)))) (if arg (save-excursion (forward-line -1) (matlab-justify-line))))) (t (message "Paragraph Fill not supported in this context.")))) ;;; Show Paren Mode support ================================================== (defun matlab-show-paren-or-block () "Function to assign to `show-paren-data-function'. Highlights parens and if/end type blocks. Returns a list: \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)" (unless (matlab-cursor-in-string-or-comment) ; Only do this if not in a string. (save-match-data (save-excursion (let ((here-beg nil) (here-end nil) (there-beg nil) (there-end nil) (mismatch nil) (noreturn nil) (here-syntax (syntax-after (point))) (here-prev-syntax (syntax-after (1- (point)))) (there-syntax nil) (here-char (char-after)) (here-prev-char (preceding-char)) (there-char nil) ) ;; Notes about fcns used here: ;; (syntax-after ) returns ( 4 c ) or ( 5 c ) ;; where 4 == open paren and 5 == close paren ;; and c is the char that closes the open or close paren ;; These checks are much faster than regexp ;; Step one - check for parens (cond ((and here-syntax (= (syntax-class here-syntax) 4)) ; open paren (setq here-beg (point) here-end (1+ (point))) (condition-case err (progn (matlab-move-simple-sexp-internal 1) (setq there-beg (- (point) 1) there-end (point) there-syntax (syntax-after there-beg) there-char (char-after there-beg)) (when (or (/= (syntax-class there-syntax) 5) (/= (cdr there-syntax) here-char) (/= (cdr here-syntax) there-char)) ; this part seems optional ;(message "ts = %S hs=%S tc = %d hc = %d" there-syntax here-syntax there-char here-char) (setq mismatch t)) ) (error (setq mismatch t)))) ((and here-prev-syntax (= (syntax-class here-prev-syntax) 5)) (setq here-beg (1- (point)) here-end (point)) (condition-case err (progn (matlab-move-simple-sexp-backward-internal 1) (setq there-end (+ (point) 1) there-beg (point) there-syntax (syntax-after there-beg) there-char (char-after there-beg)) (when (or (/= (syntax-class there-syntax) 4) (/= (cdr there-syntax) here-prev-char) (/= (cdr here-prev-syntax) there-char)) ; this part seems optional (setq mismatch t)) ) (error (setq mismatch t)))) (t ;; Part 2: Are we looking at a block start/end, such as if end; ;; If we are on a word character, or just after a ;; word character move back one symbol. This will let ;; us use the block begin / end matchers to figure ;; out where we are. (let ((startsym (matlab--mk-keyword-node)) (endsym nil) ) (when (matlab--valid-keyword-node startsym) (goto-char (nth 2 startsym)) (condition-case err (cond ((eq (car startsym) 'decl) ;; We are looking at a 'function' start. ;; Since functions may not have an end, we need ;; to handle this case special. (setq here-beg (nth 2 startsym) here-end (nth 3 startsym)) (if (matlab--scan-block-forward) ;; if context is returned, failed to find something ;; this is a mismatch if fcns don't have end. (setq mismatch t) ;; Otherwise, we found something good (setq endsym (matlab--mk-keyword-node)) (if (not endsym) ;; End of buffer on function w/ no end? (if matlab-functions-have-end (setq mismatch t) (setq mismatch nil)) ;; Found a symbol (setq there-beg (nth 2 endsym) there-end (nth 3 endsym) mismatch nil) ) )) ;; Misc block starts ((memq (car startsym) '(ctrl args mcos)) ;; We are at the beginning of a block. Navigate forward to the end ;; statement. (setq here-beg (nth 2 startsym) here-end (nth 3 startsym)) (if (matlab--scan-block-forward) (setq mismatch t) (setq endsym (matlab--mk-keyword-node)) (if (not endsym) (setq mismatch t) ;; Found a symbol (setq there-beg (nth 2 endsym) there-end (nth 3 endsym) mismatch nil)))) ;; Misc block middles an ends ((memq (car startsym) '(end mid case)) ;; We are at the end of a block or on a middle keyword ;; like else, catch, or case. In all these go to beginning. (setq here-beg (nth 2 startsym) here-end (nth 3 startsym)) (goto-char here-beg) (if (matlab--scan-block-backward-up) (setq mismatch t) (setq endsym (matlab--mk-keyword-node)) (if (not endsym) (setq mismatch t) ;; Found a symbol (setq there-beg (nth 2 endsym) there-end (nth 3 endsym) mismatch nil)))) ;; No block matches, just return nothing. (t (setq noreturn t)) ) ;; An error occurred. Assume 'here-*' is set, and setup mismatch. (error (setq mismatch t)))) ))) (if noreturn nil (list here-beg here-end there-beg there-end mismatch) )))))) ;;; Electric pair mode ============================================ (defun matlab-electric-pair-inhibit-predicate (char) "Return non-nil if `electric-pair-mode' should not pair this char." (or (funcall 'electric-pair-default-inhibit char) (cond ((and (eq char ?') (progn (forward-char -1) (looking-at "\\w\\|\\s_\\|\\."))) t) )) ) ;;; M Code verification ============================================ (defun matlab-toggle-show-mlint-warnings () "Toggle `matlab-show-mlint-warnings'." (interactive) (setq matlab-show-mlint-warnings (not matlab-show-mlint-warnings)) (if matlab-highlight-cross-function-variables (if matlab-show-mlint-warnings (mlint-buffer) ; became true, recompute mlint info (mlint-clear-warnings))) ; became false, just remove highlighting ;; change mlint mode altogether (mlint-minor-mode (if (or matlab-highlight-cross-function-variables matlab-show-mlint-warnings) 1 -1))) (defun matlab-toggle-highlight-cross-function-variables () "Toggle `matlab-highlight-cross-function-variables'." (interactive) (setq matlab-highlight-cross-function-variables (not matlab-highlight-cross-function-variables)) (if matlab-show-mlint-warnings (if matlab-highlight-cross-function-variables (mlint-buffer) ; became true, recompute mlint info ; became false, just remove highlighting ... (mlint-clear-cross-function-variable-highlighting))) (mlint-minor-mode (if (or matlab-highlight-cross-function-variables matlab-show-mlint-warnings) 1 -1))) ; change mlint mode altogether ;;; Verify / Auto-fix ============================================ (defun matlab-mode-verify-fix-file-fn () "Verify the current buffer from `write-contents-hooks'." (if matlab-verify-on-save-flag (matlab-mode-verify-fix-file (> (point-max) matlab-block-verify-max-buffer-size))) ;; Always return nil. nil) (defun matlab-mode-verify-fix-file (&optional fast) "Verify the current buffer satisfies all M things that might be useful. We will merely loop across a list of verifiers/fixers in `matlab-mode-verify-fix-functions'. If optional FAST is non-nil, do not perform usually lengthy checks." (interactive) (save-excursion ;; Always re-validate if functions have end. (matlab-mode-vf-guess-functions-have-end fast) ;; Loop over the options. (mapc (lambda (func) (funcall func fast)) matlab-mode-verify-fix-functions)) (if (matlab-called-interactively-p) (message "Done."))) ;; ;; Add more auto verify/fix functions here! ;; (defun matlab-mode-vf-guess-functions-have-end (&optional fast) "Look at the current buffer state and decide determine if functions have end. If this is already known, no action is taken." (let ((filetype (matlab-guess-script-type))) ;; Lets if the file if we were in still doesn't know what to do ;; a bout ends, and re-assert what we should do. (cond ;; If the file is empty of code (from before, or just now) ;; then optimize out this step. ((eq filetype 'empty) ;; If user deleted content, go back into guess mode. (setq matlab-functions-have-end 'guess) (matlab-functions-have-end-minor-mode 1) ) ;; If there is just bad syntax somewhere, skip it with a notice. ((save-excursion (goto-char (point-max)) (matlab-in-list-p)) (setq matlab-functions-have-end 'guess) (matlab-functions-have-end-minor-mode 1) (message "Unterminated list - skipping block check")) ;; If we are in guess mode, but user added content, we can ;; not have a fresh new guess. ((eq matlab-functions-have-end 'guess) (let ((guess (matlab-do-functions-have-end-p 'no-navigate))) (if guess (matlab-functions-have-end-minor-mode 1) (matlab-functions-have-end-minor-mode -1))) ) ;; If we are in no-end mode, BUT the filetype is wrong, say something. ((and (not matlab-functions-have-end) (or (eq filetype 'script) (eq filetype 'class))) (message "Type of file detected no longer matches `matlab-functions-have-end' of nil, assume t.") (matlab-functions-have-end-minor-mode 1) (sit-for 1) ) ;; If functions have end but the style changes, re-up the lighter on the minor mode. ;; note, we can ignore that 'empty == 'guess b/c handled earlier. ((and matlab-functions-have-end (not (eq matlab-functions-have-end filetype))) (matlab-functions-have-end-minor-mode 1)) ;; If the variable was specified and file is not empty, then do nothing. ;; TODO - maybe we should force to t for scripts and classes? ) ;; end cond )) (defun matlab-mode-vf-functionname (&optional fast) "Verify/Fix the function name of this file. Optional argument FAST is ignored." (matlab-navigation-syntax (goto-char (point-min)) (matlab-find-code-line) (let ((func nil) (bn (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) (fcn (matlab-line-declaration-name))) (when (and fcn (eq (car fcn) 'function) (not (string= (nth 1 fcn) bn))) (unless (not (matlab-mode-highlight-ask (nth 2 fcn) (nth 3 fcn) "Function name and file names are different. Fix function name?")) (goto-char (nth 2 fcn)) (delete-region (nth 2 fcn) (nth 3 fcn)) (insert bn)))))) (defun matlab-mode-vf-classname (&optional fast) "Verify/Fix the class name of this file. Optional argument FAST is ignored." (matlab-navigation-syntax (goto-char (point-min)) (matlab-find-code-line) (let ((class nil) (bn (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) (class (matlab-line-declaration-name))) (when (and class (eq (car class) 'classdef) (not (string= (nth 1 class) bn))) (unless (not (matlab-mode-highlight-ask (nth 2 class) (nth 3 class) "Class name and file names are different. Fix class name?")) (goto-char (nth 2 class)) (delete-region (nth 2 class) (nth 3 class)) (insert bn)))))) (defun matlab-mode-vf-add-ends (&optional fast) "Verify/Fix adding ENDS to functions. Optional argument FAST skips this test in fast mode." ;; We used to do extra checking here, but now we do ;; checking in the verifier (when (not fast) (matlab-mode-vf-block-matches-forward nil t) )) (defun matlab-mode-vf-block-matches-forward (&optional fast addend) "Verify/Fix unterminated (or un-ended) blocks. This only checks block regions like if/end. If `matlab-mode-vf-add-ends' is part of your verify list, this will not be needed. Optional argument FAST causes this check to be skipped. Optional argument ADDEND asks to add ends to functions, and is used by `matlab-mode-vf-add-ends'" (let ((expr nil) (scanstate nil) (exit nil) ;; lets avoid asking questions based on id of this file ;; and if ends are optional in the first place. (filetype (matlab-guess-script-type)) ) ;; Before checking syntax, lets re-look at the file if we were in ;; guess mode and re-assert what we should do. (when (or (eq filetype 'empty) (save-excursion (goto-char (point-max)) (matlab-in-list-p))) ;; In a bad state - go fast. (setq fast t)) ;; Navigate our sexp's and make sure we're all good. (matlab-navigation-syntax (goto-char (point-min)) (while (and (not fast) (not exit) (matlab--scan-next-keyword 'decl (point-max))) (forward-word -1) (if (not (setq scanstate (matlab--scan-block-forward))) ;; This scan returns any leftover unterminated blocks. ;; If it is empty, we are fine. (when (eq matlab-functions-have-end 'guess) (matlab-functions-have-end-minor-mode 1)) ;; If we had an error, but none of the above, try to fix? ;; Get locations out of the found keyword. (save-excursion (let ((s (nth 2 (car scanstate))) (e (nth 3 (car scanstate)))) (goto-char s) ;; Try to add an end to the broken block (if addend (if (matlab-mode-highlight-ask s e "Unterminated block. Try to add end?") (matlab-mode-vf-add-end-to-this-block) ;; Else, mark this buffer as not needing ends, ;; but ONLY if a function buffer (when (eq filetype 'function) (if (matlab-mode-highlight-ask s e "Should functions have end in this file?") (matlab-functions-have-end-minor-mode 1) (matlab-functions-have-end-minor-mode -1) (message "Marking buffer as not needing END for this session.") (sit-for 1)))) ;; We aren't in addend mode then we are in plain verify ;; mode (if (matlab-mode-highlight-ask s e "Unterminated block. Continue anyway?") nil ;; continue anyway. (error "Unterminated Block found!"))) ))) ;; save-excursion, let, if (message "Block-check: %d%%" (/ (/ (* 100 (point)) (point-max)) 2)))))) (defun matlab-mode-vf-add-end-to-this-block () "Add an end to the current block the cursor is on." ;; Our best guess is just in front of a 'function' block, or at the end ;; of the current buffer. (save-excursion (end-of-line) (if (re-search-forward "^function " nil t) (progn (beginning-of-line) (save-excursion (insert "end\n\n")) (matlab-indent-line)) (goto-char (point-max)) (save-excursion (insert "\nend\n\n")) (matlab-indent-line)))) ;;; Utility for verify/fix actions if you need to highlight ;; a section of the buffer for the user's approval. (defun matlab-mode-highlight-ask (begin end prompt) "Highlight from BEGIN to END while asking PROMPT as a yes-no question." (let ((mo (matlab-make-overlay begin end (current-buffer))) (show-paren-mode nil) ;; this will highlight things we often ask about. disable. (ans nil)) (condition-case nil (progn (matlab-overlay-put mo 'face 'matlab-region-face) (setq ans (y-or-n-p prompt)) (matlab-delete-overlay mo)) (quit (matlab-delete-overlay mo) (error "Quit"))) ans)) ;;; Quiesce an M file to remove accidental display of ANS during a run. ;; Useful if you have random outputs and you don't know where they are from, ;; or before compiling to standalone where some functions now have outputs ;; that did not have outputs earlier. ;; ;; You probably don't want this as a default verify function (defvar matlab-quiesce-nosemi-regexp "\\s-*\\(function\\|parfor\\|for\\|spmd\\|while\\|try\\|catch\\|\ switch\\|otherwise\\|case\\|break\\|if\\|else\\|end\\|return\\|disp\\|\ $\\|%\\)" "Regular expression used to detect if a semicolon is needed at the end of a line.") (defun matlab-mode-vf-quiesce-buffer (&optional fast) "Find all commands that do not end in ;, and add one. This has the effect of removing any extraneous output that may not be desired. Optional argument FAST is not used." (interactive) (save-excursion (push-mark) (goto-char (point-min)) (let ((msgpos 0) (dir .2)) (while (not (save-excursion (end-of-line) (eobp))) (message (aref [ "Scanning o...." "Scanning .o..." "Scanning ..o.." "Scanning ...o." "Scanning ....o" ] (floor msgpos))) (setq msgpos (+ msgpos dir)) (if (or (> msgpos 5) (< msgpos 0)) (setq dir (- dir) msgpos (+ (* 2 dir) msgpos))) (matlab-scan-end-of-command) (when (and (matlab-line-end-of-code-needs-semicolon-p) (matlab-mode-highlight-ask (point) (+ 1 (point)) "Add Semi colon here? ")) (insert ";")) (forward-line 1)))) (message "Scanning .... done")) ;;; matlab-mode debugging ===================================================== (defun matlab-show-line-info () "Display type and attributes of current line. Used in debugging." (interactive) (matlab-navigation-syntax (let* ((msg "") (lvl2 (matlab-compute-line-context 2)) (lvl1 (matlab-get-lvl1-from-lvl2 lvl2)) (lvl1msg (matlab-describe-line-indent-context lvl1 t)) (indent nil) (fullindent (matlab--calc-indent lvl2 'indent)) (nexti (matlab-next-line-indentation lvl1)) (defn (matlab-current-defun)) ) (setq msg (concat msg "Line Syntax: " lvl1msg " | Preferred Indents: This: " (int-to-string (nth 1 indent)) " Next: " (int-to-string nexti) " Indent Style: " (symbol-name (car indent)) )) (when (matlab-line-ellipsis-p lvl1) (setq msg (concat msg " w/cont"))) (when (matlab-line-end-comment-point lvl1) (setq msg (concat msg " w/comm"))) (when defn (setq msg (concat msg " Defun: " defn))) (message "%s" msg)))) (provide 'matlab) ;; Local Variables: ;; indent-tabs-mode: nil ;; End: ;;; matlab.el ends here ;; LocalWords: Wette mwette edu Ludlam eludlam defconst compat easymenu defcustom mfiles objc elec ;; LocalWords: CASEINDENT COMMANDINDENT sexp sg Fns Alist symbolp defun mmode setq decl memq progn ;; LocalWords: elipsis vf functionname booleanp keymap torkel fboundp gud ebstop mlgud ebclear ;; LocalWords: ebstatus mlg mlgud's subjob featurep defface commanddual cellbreak cellbreaks cdr ;; LocalWords: animatedline rlim thetalim cartesian stackedplot bubblechart swarmchart wordcloud ;; LocalWords: bubblecloud heatmap parallelplot fcontour anim polarplot polarscatter polarhistogram ;; LocalWords: polarbubblechart goeplot geoscatter geobubble geodensity fimplicit fsurf tiledlayout ;; LocalWords: nexttile uicontext mld flintmax keywordlist mapconcat vardecl flb fle blockmatch bol ;; LocalWords: eol tm newmdata Classdefs dem Imenu imenu boundp alist reindent unindent vers Sexp's ;; LocalWords: Defuns fn minibuffer eobp autoend noerror returnme Unstarted parentblock defuns bobp ;; LocalWords: noprogress minibufferp bolp eolp calc funcall ci sem prevcmd DEPTHNUMBER blockstart ;; LocalWords: blockmid blockendless blockend CTXT listp fc pc boc parencol parenchar parenpt ;; LocalWords: parenindent parenopt FUNCTIONs MAXs prev startpnt depthchange bc emacsen afterd ;; LocalWords: befored okpos startlst endlst ellipsify noreturn hs tc hc startsym endsym mapc func ;; LocalWords: filetype bn nondirectory scanstate sexp's nosemi msgpos fullindent nexti defn ;; LocalWords: classdef's matlab-mode/ChangeLog.old20000664000175000017500000005276514611713120016402 0ustar sebastiensebastienThis is the old ChangeLog from CVS @ MathWorks via checkin comments. --------------- ;;; $Log$ ;;; Revision 1.1 2005-12-01 18:37:46 zappo ;;; *** empty log message *** ;;; Revision 1.4.4.11.2.4.2.2 2005/11/16 13:53:36 eludlam Small completion patch. Revision 1.4.4.11.2.4.2.1 2005/11/16 13:52:20 eludlam Small completion patch. Revision 1.4.4.11.2.4 2005/07/11 20:14:13 batserve 2005/06/27 1.4.4.14 batserve 2005/06/21 1.4.4.13.2.1 padman Saved after merging from 1.4.8.2 (Akernel) to dev_padman_A (1.4.4.13.2.0) 2005/06/21 1.4.4.13.2.2 padman Code Reviewer: TBRB mkarr,eludlam,csiegal merged Akernel,A versions some of Akernel's changes already got into A please review the merges Accepted job 28303 in A Accepted job 15746a in Ami Revision 1.4.4.11.2.5 2005/09/20 04:53:59 batserve 2005/09/08 1.4.4.15 batserve 2005/08/01 1.4.10.1 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Accepted job 28899 in A Accepted job 16896b in Ami Revision 1.4.4.15 2005/09/08 20:31:39 batserve 2005/08/01 1.4.10.1 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Accepted job 28899 in A Revision 1.4.10.1 2005/08/01 15:13:19 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Revision 1.4.4.14.6.1 2005/07/29 19:50:48 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Revision 1.4.4.11.2.5 2005/09/20 04:53:59 batserve 2005/09/08 1.4.4.15 batserve 2005/08/01 1.4.10.1 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Accepted job 28899 in A Accepted job 16896b in Ami Revision 1.4.4.15 2005/09/08 20:31:39 batserve 2005/08/01 1.4.10.1 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Accepted job 28899 in A Revision 1.4.10.1 2005/08/01 15:13:19 batserve 2005/07/29 1.4.4.14.6.1 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Accepted job 2313 in Alead Revision 1.4.4.14.6.1 2005/07/29 19:50:48 mkarr Related Records: 274559 Code Reviewer: Eric Ludlam Introduced functions-have-end minor mode. Introduced special highlighting for nested function/end. Optimize matlab-backward-sexp by assuming that sexps to not cross "function" at the left margin. Handle documentation anchors (links). Fixed a bug in highlighting cross-function variables. Fixed infinite loop bug when matching "end" to "function", in the mode where functions are not expected to have "end". Revision 1.4.4.14 2005/06/27 20:27:59 batserve 2005/06/21 1.4.4.13.2.1 padman Saved after merging from 1.4.8.2 (Akernel) to dev_padman_A (1.4.4.13.2.0) 2005/06/21 1.4.4.13.2.2 padman Code Reviewer: TBRB mkarr,eludlam,csiegal merged Akernel,A versions some of Akernel's changes already got into A please review the merges Accepted job 28303 in A Revision 1.4.4.13.2.2 2005/06/21 22:38:35 padman Code Reviewer: TBRB mkarr,eludlam,csiegal merged Akernel,A versions some of Akernel's changes already got into A please review the merges Revision 1.4.4.13.2.1 2005/06/21 22:21:29 padman Saved after merging from 1.4.8.2 (Akernel) to dev_padman_A (1.4.4.13.2.0) Revision 1.4.4.13 2005/06/21 18:51:17 batserve 2005/05/25 1.4.4.11.2.2 batserve 2005/01/21 1.4.2.12.2.2 eludlam Update copyright 2005/05/17 1.4.2.12.2.3 eludlam tic/toc indent fix. User fix for auto-fill. 2005/05/24 1.4.2.12.2.4 eludlam Related Records: 257613 258664 Code Reviewer: mkarr Fix indentation numbers. Fix tic/toc indentation w/ assignments. Integrate user patch for auto-fill of of strings. Accepted job 15048 in Ami 2005/06/04 1.4.4.11.2.3 batserve 2005/06/02 1.4.2.12.2.5 eludlam Related Records: 266250 Code Reviewer: mkarr Allow H1 lines for nested functions to indent correctly. Make comment char insertion electric. Added a few completion words. Fixed byte compilation warnings. Accepted job 15385 in Ami Accepted job 28180 in A Revision 1.4.4.11.2.3 2005/06/04 23:41:36 batserve 2005/06/02 1.4.2.12.2.5 eludlam Related Records: 266250 Code Reviewer: mkarr Allow H1 lines for nested functions to indent correctly. Make comment char insertion electric. Added a few completion words. Fixed byte compilation warnings. Accepted job 15385 in Ami Revision 1.4.2.12.2.5 2005/06/02 18:44:30 eludlam Related Records: 266250 Code Reviewer: mkarr Allow H1 lines for nested functions to indent correctly. Make comment char insertion electric. Added a few completion words. Fixed byte compilation warnings. Revision 1.4.2.12.2.4 2005/05/24 18:26:06 eludlam Related Records: 257613 258664 Code Reviewer: mkarr Fix indentation numbers. Fix tic/toc indentation w/ assignments. Integrate user patch for auto-fill of of strings. Revision 1.4.2.12.2.3 2005/05/17 14:59:06 eludlam tic/toc indent fix. User fix for auto-fill. Revision 1.4.2.12.2.2 2005/01/21 21:32:24 eludlam Update copyright Revision 1.4.2.12.2.1 2005/01/21 13:25:11 eludlam Related Records: 250151 Code Reviewer: paulk Fix mlint dependency problem. Fix XEmacs compatibility problem. Add (behind option) experimental shell completion system. Add (behind comment) hideshow support. Revision 1.4.2.12 2004/06/16 03:10:04 batserve 2004/06/10 1.4.2.11.2.1 mkarr Related Records: 168455 Code Reviewer: Ty Lightner, Eric Ludlam Changes to support nested functions. Accepted job 12607 in Amisc Revision 1.4.2.11.2.1 2004/06/10 13:47:15 mkarr Related Records: 168455 Code Reviewer: Ty Lightner, Eric Ludlam Changes to support nested functions. Revision 1.4.2.11 2004/05/13 02:52:11 batserve 2004/03/11 1.4.6.12 eludlam Saved after merging with Amisc (1.4.2.9) 2004/03/19 1.4.6.13 eludlam Related Records: 210470 Code Reviewer: Various Fix header comments. Fix XEmacs compatibility itimer start. Fix XEmacs overlay compatibility macros. Add if(false) next to if(0) special code. Add support for %{ %} block comments. Fix problem with Emacs Link when not in use, and add new config item. Add optional feature for automatically adding a semicolon when needed. Add variable for lines that don't need semicolons. Fix menu item for mlint to not require it. Add -nodesktop to the command line switches. Fix debugger overlay arrow. Add `matlab-shell-run-region-or-line' command. 2004/03/19 1.4.6.14 eludlam Saved after merging with Amisc (1.4.2.10) Accepted job 12462 in Amisc Revision 1.4.6.14 2004/03/19 21:02:02 eludlam Saved after merging with Amisc (1.4.2.10) Revision 1.4.6.13 2004/03/19 20:59:56 eludlam Related Records: 210470 Code Reviewer: Various Fix header comments. Fix XEmacs compatibility itimer start. Fix XEmacs overlay compatibility macros. Add if(false) next to if(0) special code. Add support for %{ %} block comments. Fix problem with Emacs Link when not in use, and add new config item. Add optional feature for automatically adding a semicolon when needed. Add variable for lines that don't need semicolons. Fix menu item for mlint to not require it. Add -nodesktop to the command line switches. Fix debugger overlay arrow. Add `matlab-shell-run-region-or-line' command. Revision 1.4.6.12 2004/03/11 13:42:54 eludlam Saved after merging with Amisc (1.4.2.9) Revision 1.4.6.11 2004/03/10 18:40:26 eludlam Related Records: 208705 Code Reviewer: ciolfi Version number change to 2.3.2 Fix comment for installatin. Fix matlab-end-of-command (patch from user.) Add mlint enable/disable to matlab menu. Revision 1.4.6.10 2003/07/15 16:49:31 eludlam Version number change. Added continue to keyword list. Added use contributed matlab-generate-latex command. Revision 1.4.2.9 2004/01/30 22:44:48 batserve 2004/01/28 1.4.4.9 batserve 2004/01/23 1.4.8.1 batserve 2004/01/13 1.4.2.8.2.1 mkarr Code Reviewer: elijah merged in changes 2004/01/20 1.4.2.8.2.2 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. 2004/01/22 1.4.2.8.2.3 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Accepted job 7543 in Akernel Accepted job 13127 in A Accepted job 11551 in Amisc Revision 1.4.2.10 2004/03/18 21:17:17 batserve 2004/03/14 1.4.4.10 batserve 2004/03/10 1.4.4.9.2.1 padman Saved after merging with 1.4.6.11 (dev_eludlam) Accepted job 14370 in A Accepted job 11967a in Amisc Revision 1.4.4.10 2004/03/14 15:30:34 batserve 2004/03/10 1.4.4.9.2.1 padman Saved after merging with 1.4.6.11 (dev_eludlam) Accepted job 14370 in A Revision 1.4.4.9.2.1 2004/03/10 20:30:27 padman Saved after merging with 1.4.6.11 (dev_eludlam) Revision 1.4.4.9 2004/01/28 23:06:13 batserve 2004/01/23 1.4.8.1 batserve 2004/01/13 1.4.2.8.2.1 mkarr Code Reviewer: elijah merged in changes 2004/01/20 1.4.2.8.2.2 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. 2004/01/22 1.4.2.8.2.3 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Accepted job 7543 in Akernel Accepted job 13127 in A Revision 1.4.8.2 2005/05/18 19:25:28 batserve 2005/05/02 1.4.4.11.2.1.2.1 mkarr Related Records: 231734 Code Reviewer: Eric Ludlam Allow for html anchors when matching for debugger output. Indent line beginning with end according to beginning of block, not according to the indentation of the previous line. Respond to "matlab:" anchors (evaluate an M expression). Accepted job 12245 in Akernel Revision 1.4.4.11.2.1.2.1 2005/05/02 23:35:19 mkarr Related Records: 231734 Code Reviewer: Eric Ludlam Allow for html anchors when matching for debugger output. Indent line beginning with end according to beginning of block, not according to the indentation of the previous line. Respond to "matlab:" anchors (evaluate an M expression). Revision 1.4.4.11.2.1 2005/01/23 18:39:17 batserve 2005/01/21 1.4.2.12.2.1 eludlam Related Records: 250151 Code Reviewer: paulk Fix mlint dependency problem. Fix XEmacs compatibility problem. Add (behind option) experimental shell completion system. Add (behind comment) hideshow support. Accepted job 13342 in Ami Revision 1.4.2.12.2.1 2005/01/21 13:25:11 eludlam Related Records: 250151 Code Reviewer: paulk Fix mlint dependency problem. Fix XEmacs compatibility problem. Add (behind option) experimental shell completion system. Add (behind comment) hideshow support. Revision 1.4.2.12 2004/06/16 03:10:04 batserve 2004/06/10 1.4.2.11.2.1 mkarr Related Records: 168455 Code Reviewer: Ty Lightner, Eric Ludlam Changes to support nested functions. Accepted job 12607 in Amisc Revision 1.4.2.11.2.1 2004/06/10 13:47:15 mkarr Related Records: 168455 Code Reviewer: Ty Lightner, Eric Ludlam Changes to support nested functions. Revision 1.4.2.11 2004/05/13 02:52:11 batserve 2004/03/11 1.4.6.12 eludlam Saved after merging with Amisc (1.4.2.9) 2004/03/19 1.4.6.13 eludlam Related Records: 210470 Code Reviewer: Various Fix header comments. Fix XEmacs compatibility itimer start. Fix XEmacs overlay compatibility macros. Add if(false) next to if(0) special code. Add support for %{ %} block comments. Fix problem with Emacs Link when not in use, and add new config item. Add optional feature for automatically adding a semicolon when needed. Add variable for lines that don't need semicolons. Fix menu item for mlint to not require it. Add -nodesktop to the command line switches. Fix debugger overlay arrow. Add `matlab-shell-run-region-or-line' command. 2004/03/19 1.4.6.14 eludlam Saved after merging with Amisc (1.4.2.10) Accepted job 12462 in Amisc Revision 1.4.6.14 2004/03/19 21:02:02 eludlam Saved after merging with Amisc (1.4.2.10) Revision 1.4.6.13 2004/03/19 20:59:56 eludlam Related Records: 210470 Code Reviewer: Various Fix header comments. Fix XEmacs compatibility itimer start. Fix XEmacs overlay compatibility macros. Add if(false) next to if(0) special code. Add support for %{ %} block comments. Fix problem with Emacs Link when not in use, and add new config item. Add optional feature for automatically adding a semicolon when needed. Add variable for lines that don't need semicolons. Fix menu item for mlint to not require it. Add -nodesktop to the command line switches. Fix debugger overlay arrow. Add `matlab-shell-run-region-or-line' command. Revision 1.4.6.12 2004/03/11 13:42:54 eludlam Saved after merging with Amisc (1.4.2.9) Revision 1.4.6.11 2004/03/10 18:40:26 eludlam Related Records: 208705 Code Reviewer: ciolfi Version number change to 2.3.2 Fix comment for installatin. Fix matlab-end-of-command (patch from user.) Add mlint enable/disable to matlab menu. Revision 1.4.6.10 2003/07/15 16:49:31 eludlam Version number change. Added continue to keyword list. Added use contributed matlab-generate-latex command. Revision 1.4.2.9 2004/01/30 22:44:48 batserve 2004/01/28 1.4.4.9 batserve 2004/01/23 1.4.8.1 batserve 2004/01/13 1.4.2.8.2.1 mkarr Code Reviewer: elijah merged in changes 2004/01/20 1.4.2.8.2.2 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. 2004/01/22 1.4.2.8.2.3 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Accepted job 7543 in Akernel Accepted job 13127 in A Accepted job 11551 in Amisc Revision 1.4.2.10 2004/03/18 21:17:17 batserve 2004/03/14 1.4.4.10 batserve 2004/03/10 1.4.4.9.2.1 padman Saved after merging with 1.4.6.11 (dev_eludlam) Accepted job 14370 in A Accepted job 11967a in Amisc Revision 1.4.4.10 2004/03/14 15:30:34 batserve 2004/03/10 1.4.4.9.2.1 padman Saved after merging with 1.4.6.11 (dev_eludlam) Accepted job 14370 in A Revision 1.4.4.9.2.1 2004/03/10 20:30:27 padman Saved after merging with 1.4.6.11 (dev_eludlam) Revision 1.4.4.9 2004/01/28 23:06:13 batserve 2004/01/23 1.4.8.1 batserve 2004/01/13 1.4.2.8.2.1 mkarr Code Reviewer: elijah merged in changes 2004/01/20 1.4.2.8.2.2 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. 2004/01/22 1.4.2.8.2.3 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Accepted job 7543 in Akernel Accepted job 13127 in A Revision 1.4.8.1 2004/01/23 20:35:05 batserve 2004/01/13 1.4.2.8.2.1 mkarr Code Reviewer: elijah merged in changes 2004/01/20 1.4.2.8.2.2 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. 2004/01/22 1.4.2.8.2.3 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Accepted job 7543 in Akernel Revision 1.4.2.8.2.3 2004/01/22 16:42:39 mkarr Code Reviewer: Eric Ludlam Adapt to column positions in matlab-url. Revision 1.4.2.8.2.2 2004/01/20 22:31:16 mkarr Code Reviewer: Eric Ludlam Make filter buffer-local. Put in html "rendering" and other changes for new handling of source locations. Revision 1.4.4.8 2003/05/08 19:45:08 batserve 2003/04/29 1.4.2.8 batserve 2003/04/28 1.4.6.9 eludlam Doc comment update (New file exchange link, matlab shell switches) Font lock of pragmas Whitespace mode support. Accepted job 7691 in Amisc Accepted job 6970 in A Revision 1.4.2.8 2003/04/29 22:45:52 batserve 2003/04/28 1.4.6.9 eludlam Doc comment update (New file exchange link, matlab shell switches) Font lock of pragmas Whitespace mode support. Accepted job 7691 in Amisc Revision 1.4.6.11 2004/03/10 18:40:26 eludlam Related Records: 208705 Code Reviewer: ciolfi Version number change to 2.3.2 Fix comment for installatin. Fix matlab-end-of-command (patch from user.) Add mlint enable/disable to matlab menu. Revision 1.4.6.10 2003/07/15 16:49:31 eludlam Version number change. Added continue to keyword list. Added use contributed matlab-generate-latex command. Revision 1.4.6.9 2003/04/28 13:38:38 eludlam Doc comment update (New file exchange link, matlab shell switches) Font lock of pragmas Whitespace mode support. Revision 1.4.6.8 2003/02/19 19:52:11 eludlam Exclude block verify from save hooks. Fixed if statement regexp for unreachable code. Add persistent as a keyword. Revision 1.4.6.7 2003/01/06 16:04:51 eludlam Fill paragraph: Fix to handle comment at end of buffer. matlab-shell: Fix where kill-local-variables is called. Revision 1.4.6.6 2002/10/23 14:56:42 eludlam Code Reviewer: eludlam User patch for finding the symbol at point. Revision 1.4.6.5 2002/10/03 18:55:27 eludlam Fix XEmacs itimer issue with block highlighting. Yoni Wexler contributed patch for indented tic/toc. Block highlighting tic/toc. font lock for tic toc. Revision 1.4.6.4 2002/09/27 13:31:22 eludlam Release Version 2.3. Removed LCD archive entry. Revision 1.4.6.3 2002/09/12 13:01:36 eludlam Added support for matlab-shell reading matlab's history. Patch originally posted to cssm. Revision 1.4.6.2 2002/08/08 18:23:24 eludlam User patch converts `matlab-shell-command-switches' to a list. Revision 1.4.6.1 2002/06/11 18:59:17 eludlam Fix block highlight matching for timer buffer switch case. Revision 1.4 2002/05/08 03:29:39 batserve Correct docstring to say that future versions of MATLAB may not require ellipses. Related Records: 131353 Code Reviewer: eludlam Revision 1.4 2002/05/07 20:39:36 paulk Correct docstring to say that future versions of MATLAB may not require ellipses. Related Records: 131353 Code Reviewer: eludlam Revision 1.3 2002/05/07 16:44:18 paulk - Support implied continuation (off by default) - Add a key binding for find-file-on-path to matlab-shell-mode - fix banner - Fix save-and-go to work in unsaved new buffers Related Records: 131353 Code Reviewer: eludlam Revision 1.2 2002/03/20 18:35:20 paulk Add a patch to matlab-mode from Eric Ludlam to support student edition. Related Records: 124543 Code Reviewer: eludlam Revision 1.1 2002/02/06 20:43:50 paulk Initial revision Revision 1.3 2001/09/07 16:44:38 paulk Changed "Mathworks" to "MathWorks" and "Matlab" to "MATLAB"? Related Records: 107855 Revision 1.2 2001/09/06 16:41:39 paulk save-and-go and run-region now invoke their eei equivalents when eei is active. Related Records: 99176 Code Reviewer: eludlam Revision 1.1 2001/08/31 20:45:51 paulk Initial revision matlab-mode/matlab-cgen.el0000664000175000017500000002021214611713120016443 0ustar sebastiensebastien;;; matlab-cgen.el --- In buffer code generation features (templates, etc) ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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 http://www.gnu.org/licenses/. ;;; Commentary: ;; ;; This library supports tempo templates, and other misc functions designed ;; to create code in an Emacs buffer. (require 'matlab) (require 'tempo) ;;; Code: (defvar matlab-tempo-tags nil "List of templates used in MATLAB mode.") ;; This trick allows this file to be autoloaded ONLY when the user uses the insert prefix. ;; ;;;###autoload (autoload 'matlab-insert-map-fcn "matlab-cgen" "Keymap for C-c C-c in matlab-mode" t 'keymap) (defvar matlab-insert-map (let ((km (make-sparse-keymap))) (define-key km "c" 'matlab-insert-next-case) (define-key km "e" 'matlab-insert-end-block) (define-key km "i" 'tempo-template-matlab-if) (define-key km "I" 'tempo-template-matlab-if-else) (define-key km "f" 'tempo-template-matlab-for) (define-key km "s" 'tempo-template-matlab-switch) (define-key km "t" 'tempo-template-matlab-try) (define-key km "w" 'tempo-template-matlab-while) (define-key km "F" 'tempo-template-matlab-function) (define-key km "'" 'matlab-stringify-region) ;; Not really inserts, but auto coding stuff (define-key km "\C-s" 'matlab-ispell-strings-and-comments) km) "Keymap used for inserting simple texts based on context.") (defvar matlab-insert-map-fcn nil "Keymap for C-c C-c in matlab-mode.") (fset 'matlab-insert-map-fcn (setq matlab-insert-map-fcn matlab-insert-map)) (add-hook 'matlab-mode-hook #'matlab-cgen-hook-fcn t) (defun matlab-cgen-hook-fcn () "Hook run in `matlab-mode-hook' needed for cgen support." ;; Tempo tags (make-local-variable 'tempo-local-tags) (setq tempo-local-tags (append matlab-tempo-tags tempo-local-tags)) ) ;; We might load late, so loop over all matlab buffers, and run the hook. (dolist (B (buffer-list)) (with-current-buffer B (when (eq major-mode 'matlab-mode) (matlab-cgen-hook-fcn)))) ;;; Templates and smart code gen features ;; (defun matlab-insert-end-block (&optional reindent) "Insert and END block based on the current syntax. Optional argument REINDENT indicates if the specified block should be re-indented." (interactive "P") (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 (progn (matlab-backward-sexp t) (setq begin (point) valid (buffer-substring-no-properties (point) (save-excursion (re-search-forward "[\n,;.]" nil t) (point))))) (error (setq valid nil)))) (if (not valid) (error "No block to end") (insert "end") (if (stringp valid) (insert " % " valid)) (matlab-indent-line) (if reindent (indent-region begin (point) nil))))) (tempo-define-template "matlab-for" '("for " p "=" p "," > n> r> & "end" > %) "for" "Insert a MATLAB for statement" 'matlab-tempo-tags ) (tempo-define-template "matlab-while" '("while (" p ")," > n> r> & "end" > %) "while" "Insert a MATLAB while statement" 'matlab-tempo-tags ) (tempo-define-template "matlab-if" '("if " p > n r> "end" > n) "if" "Insert a MATLAB if statement" 'matlab-tempo-tags ) (tempo-define-template "matlab-if-else" '("if " p > n r> "else" > n "end" > n) "if" "Insert a MATLAB if statement" 'matlab-tempo-tags ) (tempo-define-template "matlab-try" '("try " > n r> "catch" > n p > n "end" > n) "try" "Insert a MATLAB try catch statement" 'matlab-tempo-tags ) (tempo-define-template "matlab-switch" '("switch " p > n "otherwise" > n r> "end" > n) "switch" "Insert a MATLAB switch statement with region in the otherwise clause." 'matlab-tempo-tags) (defun matlab-insert-next-case () "Insert a case statement inside this switch statement." (interactive) ;; First, make sure we are where we think we are. (let ((valid t)) (save-excursion (condition-case nil (progn (matlab-backward-sexp t) (setq valid (looking-at "switch"))) (error (setq valid nil)))) (if (not valid) (error "Not in a switch statement"))) (when (not (matlab-line-empty-p (matlab-compute-line-context 1))) (end-of-line) (insert "\n")) (indent-to 0) (insert "case ") (matlab-indent-line)) (tempo-define-template "matlab-function" '("function " (P "output argument(s): " output t) ;; Insert brackets only if there is more than one output argument (if (string-match "," (tempo-lookup-named 'output)) '(l "[" (s output) "]") '(l (s output))) ;; Insert equal sign only if there is output argument(s) (if (= 0 (length (tempo-lookup-named 'output))) nil " = ") ;; The name of a function, as defined in the first line, should ;; be the same as the name of the file without .m extension (if (= 1 (count-lines 1 (point))) (tempo-save-named 'fname (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))) '(l (P "function name: " fname t))) (tempo-lookup-named 'fname) "(" (P "input argument(s): ") ")" n "% " (upcase (tempo-lookup-named 'fname)) " - " (P "H1 line: ") n "% " p n (if matlab-functions-have-end '(l "end" n))) "function" "Insert a MATLAB function statement" 'matlab-tempo-tags ) (defun matlab-stringify-region (begin end) "Put MATLAB 's around region, and quote all quotes in the string. Stringification allows you to type in normal MATLAB code, mark it, and then turn it into a MATLAB string that will output exactly what's in the region. BEGIN and END mark the region to be stringified." (interactive "r") (save-excursion (goto-char begin) (if (re-search-forward "\n" end t) (error "You may only stringify regions that encompass less than one line")) (let ((m (make-marker))) (move-marker m end) (goto-char begin) (insert "'") (while (re-search-forward "'" m t) (insert "'")) (goto-char m) (insert "'")))) ;;; SPELLING ;; (defun matlab-ispell-strings-and-comments-region (begin end) "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)))) )) (defun matlab-ispell-strings-and-comments () "Spell check valid strings in the current buffer with Ispell. Calls `matlab-ispell-strings-region'" (interactive) (matlab-ispell-strings-and-comments-region (point-min) (point-max))) ;;; Printing ;; ;;;###autoload (defun matlab-generate-latex () "Convert a MATLAB M file into a Latex document for printing. Author: Uwe Brauer oub@eucmos.sim.ucm.es Created: 14 Feb 2002" (interactive "*") (save-restriction (save-excursion (goto-char (point-min)) (insert "\\documentclass[12pt]{report}\n \\usepackage{listings} \\lstloadlanguages{Matlab} \\lstset{language=Matlab,keywordstyle=\\bfseries,labelstep=1,escapechar=\\#} \\begin{document} \\begin{lstlisting}{}") (newline) (goto-char (point-max)) (insert "\n\\end{lstlisting}\n\\end{document}") (widen))) (font-lock-mode nil) (LaTeX-mode) (font-lock-mode nil)) (provide 'matlab-cgen) ;;; matlab-cgen.el ends here matlab-mode/COPYING0000664000175000017500000010451514611713120015013 0ustar sebastiensebastien GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . matlab-mode/templates/0000775000175000017500000000000014611713120015750 5ustar sebastiensebastienmatlab-mode/templates/Makefile0000664000175000017500000000102214611713120017403 0ustar sebastiensebastien# Automatically Generated Makefile by EDE. # For use with: make # Relative File Name: templates/Makefile # # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST. # EDE is the Emacs Development Environment. # http://cedet.sourceforge.net/ede.shtml # top=../ ede_FILES=Project.ede Makefile templates_MISC=srecode-matlab.srt VERSION=4.0 DISTDIR=$(top)matlab-emacs-$(VERSION)/templates all: templates templates: @ tags: .PHONY: dist dist: mkdir $(DISTDIR) cp $(templates_MISC) $(ede_FILES) $(DISTDIR) # End of Makefile matlab-mode/templates/Project.ede0000664000175000017500000000041714611713120020037 0ustar sebastiensebastien;; Object Templates ;; EDE project file. (ede-proj-project "Templates" :name "Templates" :file "Project.ede" :targets (list (ede-proj-target-makefile-miscelaneous "templates" :name "templates" :path "" :source '("srecode-matlab.srt") ) ) ) matlab-mode/templates/srecode-matlab.srt0000664000175000017500000000475514611713120021377 0ustar sebastiensebastien;; srecode-matlab.srt --- SREcode Templates for MATLAB. ;; Copyright (C) 2008, 2014 Eric Ludlam ;; Author: Eric Ludlam ;; X-RCS: $Id$ ;; 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 2, 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; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. set mode "matlab-mode" set escape_start "{{" set escape_end "}}" context file set comment_start "%" set comment_prefix "%" set comment_end "" ;; @todo - add argument support. ;; choose template based on script/function or class in current dir. template empty :matlab :file :user :time "Fill out an empty file." ---- {{#FILE_FUNCTION}}function {{FILE_SYMBOL}} {{comment_prefix}} {{FILE_DOC_SYMBOL}} - {{comment_prefix}} {{comment_prefix}} Copyright (C) {{YEAR}} {{?AUTHOR}} {{^}} end {{/FILE_FUNCTION}}{{#FILE_CLASS}}classdef {{FILE_SYMBOL}} {{comment_prefix}} Class {{FILE_DOC_SYMBOL}} {{comment_prefix}} {{comment_prefix}} Copyright (C) {{YEAR}} {{?AUTHOR}} properties end methods function h = {{FILE_SYMBOL}}({{^}}) % Constructor end end {{/FILE_CLASS}} end ---- bind "e" context declaration prompt NAME "Name for declaration: " template classdef :time :user :time "Template to declare a class" ---- classdef {{?NAME}} % Class {{NAME:upcase}} % % Copyright (C) {{YEAR}} {{?AUTHOR}} properties end methods function h = {{NAME}}(varargin) {{^}} end end end ---- bind "c" template function :blank :indent "Template to declare a function." ---- function {{?NAME}} {{^}} end ---- bind "f" template variable :blank :indent "Template to declare a variable." ---- global {{?NAME}} ---- bind "v" context code template function :blank :indent "Nested functions are indented." ---- function {{?NAME}} {{^}} end ---- bind "f" template variable :blank :indent "Template to declare a variable." ---- persistent {{?NAME}} ---- bind "v" ;; endmatlab-mode/matlab-compat.el0000664000175000017500000002421314625574731017041 0ustar sebastiensebastien;;; matlab-compat.el --- Compatibility Code ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; To support a wide range of different Emacs versions, these compat ;; functions will hide away the shims needed to work cross platform. ;;; Code: (eval-and-compile (if (string-match "X[Ee]macs" emacs-version) (progn (defalias 'matlab-make-overlay 'make-extent) (defalias 'matlab-overlay-put 'set-extent-property) (defalias 'matlab-overlay-get 'extent-property) (defalias 'matlab-delete-overlay 'delete-extent) (defalias 'matlab-overlay-start 'extent-start-position) (defalias 'matlab-overlay-end 'extent-end-position) (defalias 'matlab-previous-overlay-change 'previous-extent-change) (defalias 'matlab-next-overlay-change 'next-extent-change) (defalias 'matlab-overlays-at (lambda (pos) (when (fboundp 'extent-list) (extent-list nil pos pos)))) (defalias 'matlab-cancel-timer 'delete-itimer) (defun matlab-run-with-idle-timer (secs repeat function &rest args) (condition-case nil (apply 'start-itimer "matlab" function secs (if repeat secs nil) t t (car args))) (error ;; If the above doesn't work, then try this old version of ;; start itimer. (when (fboundp 'start-itimer) (start-itimer "matlab" function secs (if repeat secs nil))))) ) ;; Else GNU Emacs (defalias 'matlab-make-overlay 'make-overlay) (defalias 'matlab-overlay-put 'overlay-put) (defalias 'matlab-overlay-get 'overlay-get) (defalias 'matlab-delete-overlay 'delete-overlay) (defalias 'matlab-overlay-start 'overlay-start) (defalias 'matlab-overlay-end 'overlay-end) (defalias 'matlab-previous-overlay-change 'previous-overlay-change) (defalias 'matlab-next-overlay-change 'next-overlay-change) (defalias 'matlab-overlays-at 'overlays-at) (defalias 'matlab-cancel-timer 'cancel-timer) (defalias 'matlab-run-with-idle-timer 'run-with-idle-timer) )) ;;; Helper aliases to suppress compiler warnings =============================== (eval-and-compile ;; `set-face-underline-p' is an obsolete function (as of 24.3); use `set-face-underline' instead. (cond ((fboundp 'set-face-underlined) (defalias 'matlab-set-face-underline 'set-face-underlined)) (t (defalias 'matlab-set-face-underline 'set-face-underline-p))) ;; `set-face-bold-p' is an obsolete function (as of 24.4); use `set-face-bold' instead. (cond ((fboundp 'set-face-bold) (defalias 'matlab-set-face-bold 'set-face-bold)) (t (defalias 'matlab-set-face-bold 'set-face-bold-p))) ;; `default-fill-column' is an obsolete variable (as of 23.2); use `fill-column' instead. (cond ((boundp 'fill-column) (defvaralias 'matlab-fill-column 'fill-column)) (t (defvaralias 'matlab-fill-column 'default-fill-column))) ;; `interactive-p' is an obsolete function (as of 23.2); use `called-interactively-p' instead. (defun matlab-called-interactively-p-helper () (called-interactively-p 'interactive)) (cond ((fboundp 'called-interactively-p) (defalias 'matlab-called-interactively-p 'matlab-called-interactively-p-helper)) (t (defalias 'matlab-called-interactively-p 'interactive-p))) ;; `toggle-read-only' is an obsolete function (as of 24.3); use `read-only-mode' instead. ;; (matlab-read-only-mode -1) ==> make writable ;; (matlab-read-only-mode 1) ==> make read-only (cond ((fboundp 'read-only-mode) (defalias 'matlab-read-only-mode 'read-only-mode)) (t (defalias 'matlab-read-only-mode 'toggle-read-only))) (cond ((fboundp 'point-at-bol) (defalias 'matlab-point-at-bol 'point-at-bol) (defalias 'matlab-point-at-eol 'point-at-eol)) ;; Emacs 20.4 ((fboundp 'line-beginning-position) (defalias 'matlab-point-at-bol 'line-beginning-position) (defalias 'matlab-point-at-eol 'line-end-position)) (t (defmacro matlab-point-at-bol () (save-excursion (beginning-of-line) (point))) (defmacro matlab-point-at-eol () (save-excursion (end-of-line) (point))))) ) ;; Keymaps (if (fboundp 'set-keymap-parent) (defalias 'matlab-set-keymap-parent 'set-keymap-parent) ;; 19.31 doesn't have set-keymap-parent (eval-when-compile (require 'comint)) (defun matlab-set-keymap-parent (keymap parent) "Set KEYMAP's parent to be PARENT." (nconc keymap comint-mode-map))) ;; String trim (if (locate-library "subr-x") (progn (require 'subr-x) (defalias 'matlab-string-trim 'string-trim) ) (defsubst matlab-string-trim (string &optional regexp) "Trim STRING of leading string matching REGEXP. REGEXP defaults to \"[ \\t\\n\\r]+\"." (let ((out string) (regexp_ (or regexp "[ \t\n\r]+"))) (when (string-match (concat "\\`\\(?:" regexp_ "\\)") out) (setq out (substring out (match-end 0)))) (when (string-match (concat "\\(?:" regexp_ "\\)\\'") out) (setq out (substring out 0 (match-beginning 0)))) 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. Return the base directory it is in." (let ((dir nil)) (dolist (P exec-path) (let ((nm (expand-file-name program P))) (when (and (file-exists-p nm) (file-executable-p nm)) (let* ((fa (file-attributes nm)) (lnk (car fa))) ;; The car is t for a directory, a string for a link, nil otherwise (if (stringp lnk) ;; We have a link - use that as our directory. (setq dir (file-name-directory lnk)) ;; No link - just use this path. (setq dir P))) ))) dir)) ;; Completion Tools (defun matlab-display-completion-list (completions common-substring) "Method for displaying COMPLETIONS with a COMMON-SUBSTRING." ;; In emacs 24.4 the common-substring is no longer needed. (let ((args (if (or (< emacs-major-version 24) (and (= emacs-major-version 24) (< emacs-minor-version 4))) (list completions common-substring) (list completions)))) (apply 'display-completion-list args))) ;; Font lock (require 'font-lock) (unless (fboundp 'font-lock-ensure) (defalias 'font-lock-ensure 'font-lock-fontify-buffer)) ;; CEDET compat if CEDET isn't around (condition-case nil (progn (require 'pulse) ) (error (defun pulse-momentary-highlight-region (start end &optional face) "Compat impl of pulse command." nil))) ;; EIEIO compatibility (condition-case nil (progn (require 'eieio) (unless (fboundp 'cl-defgeneric) ;; We are in an antique Emacs that uses the old eieio. (defalias 'cl-defmethod 'defmethod) ) (unless (fboundp 'cl-call-next-method) ;; We are in an antique Emacs that uses the old eieio. (defalias 'cl-call-next-method 'call-next-method) ) ) (error (message "EIEIO not available. Only MATLAB editing enabled."))) ;;; Finding EmacsClient (defun matlab-find-emacsclient () "Locate the emacsclient correspoinding to the current emacs binary defined by `invocation-name' in `invocation-directory'" (let ((ec "emacsclient")) (cond ;; Mac ((equal system-type 'darwin) (if (file-exists-p (concat invocation-directory "emacsclient")) ;; running the default emacs? (setq ec (concat invocation-directory "emacsclient")) ;; On Mac, one can install into ;; /Applications/Emacs.app/Contents/MacOS/Emacs ;; /Applications/Emacs.app/Contents/MacOS/bin/emacsclient (if (file-exists-p (concat invocation-directory "bin/emacsclient")) (setq ec (concat invocation-directory "bin/emacsclient"))))) ;; Windows ((equal system-type 'windows-nt) (if (file-exists-p (concat invocation-directory "emacsclientw.exe")) (setq ec (concat invocation-directory "emacsclientw.exe")) (error "unable to locate emacsclientw.exe. It should be in %s" invocation-directory))) ;; Linux or other UNIX system (t ;; Debian 9 can be setup to have: ;; /usr/bin/emacs ;; /usr/bin/emacsclient ;; /usr/bin/emacs24 ;; /usr/bin/emacsclient.emacs24 ;; /usr/bin/emacs25 ;; /usr/bin/emacsclient.emacs25 (if (and (equal invocation-name "emacs") (file-exists-p (concat invocation-directory "emacsclient"))) (setq ec (concat invocation-directory "emacsclient")) (if (file-exists-p (concat invocation-directory "emacsclient." invocation-name)) (setq ec (concat invocation-directory "emacsclient." invocation-name)))))) ;; Return, ec, the emacsclient to use ec )) (when (not (fboundp 'string-replace)) ;; string-replace appeared in Emacs 28 (defun string-replace (fromstring tostring instring) (let ((case-fold-search nil)) (replace-regexp-in-string (regexp-quote fromstring) tostring instring t t)))) (provide 'matlab-compat) ;;; matlab-compat.el ends here ;; LocalWords: el Ludlam eludlam osboxes Ee progn defalias fboundp itimer ;; LocalWords: defun boundp defvaralias bol eol defmacro Keymaps keymap comint ;; LocalWords: KEYMAP's nconc dolist nm lnk setq matlab-mode/matlab-complete.el0000664000175000017500000005512014611713120017345 0ustar sebastiensebastien;;; matlab-complete.el --- Simple completion tool for matlab-mode ;; ;; Copyright (C) 2019, 2020 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; This library supports a simple completion technique for MATLAB. ;; ;; It uses the following techniques: ;; * Lists of symbols that can be completed (from ~ 2004ish) ;; * scan buffer for functions and symbols. ;; * look for local files that might be functions. ;; ;; Moved to separate library, as some modern tools do a better job ;; 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 =========================================================== ;; (defcustom matlab-completion-technique 'complete "*How the `matlab-complete-symbol' interfaces with the user. Valid values are: 'increment - which means that new strings are tried with each successive call until all methods are exhausted. (Similar to `hippie-expand'.) 'complete - Which means that if there is no single completion, then all possibilities are displayed in a completion buffer." :group 'matlab :type '(radio (const :tag "Incremental completion (hippie-expand)." increment) (const :tag "Show completion buffer." complete))) ;;; Lists for matlab keywords ================================================= (defvar matlab-keywords-solo '("break" "case" "else" "elseif" "end" "for" "parfor" "function" "if" "tic" "toc" "otherwise" "profile" "switch" "while" "try" "catch" "spmd") "Keywords that appear on a line by themselves.") (defvar matlab-keywords-return '("acos" "acosh" "acot" "acoth" "acsch" "asech" "asin" "asinh" "atan" "atan2" "atanh" "cos" "cosh" "coth" "csc" "csch" "exp" "log" "log10" "log2" "sec" "sech" "sin" "sinh" "tanh" "abs" "sign" "sqrt" ) "List of MATLAB keywords that have return arguments. This list still needs lots of help.") (defvar matlab-keywords-boolean '("all" "any" "exist" "isempty" "isequal" "ishold" "isfinite" "isglobal" "isinf" "isletter" "islogical" "isnan" "isprime" "isreal" "isspace" "logical" "isa") "List of keywords that are typically used as boolean expressions.") (defvar matlab-core-properties '("ButtonDownFcn" "Children" "Clipping" "CreateFcn" "DeleteFcn" "BusyAction" "HandleVisibility" "HitTest" "Interruptible" "Parent" "Selected" "SelectionHighlight" "Tag" "Type" "UIContextMenu" "UserData" "Visible") "List of properties belonging to all HG objects.") (defvar matlab-property-lists '(("root" . ("CallbackObject" "Language" "CurrentFigure" "Diary" "DiaryFile" "Echo" "ErrorMessage" "Format" "FormatSpacing" "PointerLocation" "MonitorPositions" "PointerWindow" "Profile" "ProfileFile" "ProfileCount" "ProfileInterval" "RecursionLimit" "ScreenDepth" "ScreenSize" "ShowHiddenHandles" "TerminalHideGraphCommand" "TerminalOneWindow" "TerminalDimensions" "TerminalProtocol" "TerminalShowGraphCommand" "Units" "AutomaticFileUpdates" )) ("axes" . ("AmbientLightColor" "Box" "CameraPosition" "CameraPositionMode" "CameraTarget" "CameraTargetMode" "CameraUpVector" "CameraUpVectorMode" "CameraViewAngle" "CameraViewAngleMode" "CLim" "CLimMode" "Color" "CurrentPoint" "ColorOrder" "DataAspectRatio" "DataAspectRatioMode" "DrawMode" "FontAngle" "FontName" "FontSize" "FontUnits" "FontWeight" "GridLineStyle" "Layer" "LineStyleOrder" "LineWidth" "NextPlot" "PlotBoxAspectRatio" "PlotBoxAspectRatioMode" "Projection" "Position" "TickLength" "TickDir" "TickDirMode" "Title" "Units" "View" "XColor" "XDir" "XGrid" "XLabel" "XAxisLocation" "XLim" "XLimMode" "XScale" "XTick" "XTickLabel" "XTickLabelMode" "XTickMode" "YColor" "YDir" "YGrid" "YLabel" "YAxisLocation" "YLim" "YLimMode" "YScale" "YTick" "YTickLabel" "YTickLabelMode" "YTickMode" "ZColor" "ZDir" "ZGrid" "ZLabel" "ZLim" "ZLimMode" "ZScale" "ZTick" "ZTickLabel" "ZTickLabelMode" "ZTickMode")) ("figure" . ("BackingStore" "CloseRequestFcn" "Color" "Colormap" "CurrentAxes" "CurrentCharacter" "CurrentObject" "CurrentPoint" "Dithermap" "DithermapMode" "FixedColors" "IntegerHandle" "InvertHardcopy" "KeyPressFcn" "MenuBar" "MinColormap" "Name" "NextPlot" "NumberTitle" "PaperUnits" "PaperOrientation" "PaperPosition" "PaperPositionMode" "PaperSize" "PaperType" "Pointer" "PointerShapeCData" "PointerShapeHotSpot" "Position" "Renderer" "RendererMode" "Resize" "ResizeFcn" "SelectionType" "ShareColors" "Units" "WindowButtonDownFcn" "WindowButtonMotionFcn" "WindowButtonUpFcn" "WindowStyle")) ("image" . ("CData" "CDataMapping" "EraseMode" "XData" "YData")) ("light" . ("Position" "Color" "Style")) ("line" . ("Color" "EraseMode" "LineStyle" "LineWidth" "Marker" "LineSmoothing" "MarkerSize" "MarkerEdgeColor" "MarkerFaceColor" "XData" "YData" "ZData")) ("patch" . ("CData" "CDataMapping" "FaceVertexCData" "EdgeColor" "EraseMode" "FaceColor" "Faces" "LineStyle" "LineWidth" "Marker" "LineSmoothing" "MarkerEdgeColor" "MarkerFaceColor" "MarkerSize" "Vertices" "XData" "YData" "ZData" "FaceLighting" "EdgeLighting" "BackFaceLighting" "AmbientStrength" "DiffuseStrength" "SpecularStrength" "SpecularExponent" "SpecularColorReflectance" "VertexNormals" "NormalMode")) ("surface" . ("CData" "CDataMapping" "EdgeColor" "EraseMode" "FaceColor" "LineStyle" "LineWidth" "Marker" "MarkerEdgeColor" "LineSmoothing" "MarkerFaceColor" "MarkerSize" "MeshStyle" "XData" "YData" "ZData" "FaceLighting" "EdgeLighting" "BackFaceLighting" "AmbientStrength" "DiffuseStrength" "SpecularStrength" "SpecularExponent" "SpecularColorReflectance" "VertexNormals" "NormalMode")) ("text\\|title\\|xlabel\\|ylabel\\|zlabel" . ("Color" "EraseMode" "Editing" "Extent" "FontAngle" "FontName" "FontSize" "FontUnits" "FontWeight" "HorizontalAlignment" "BackgroundColor" "EdgeColor" "Margin" "Position" "Rotation" "String" "Units" "Interpreter" "VerticalAlignment")) ("uicontextmenu" . ("Callback")) ("uicontrol" . ("BackgroundColor" "Callback" "CData" "Enable" "Extent" "FontAngle" "FontName" "FontSize" "FontUnits" "FontWeight" "ForegroundColor" "HorizontalAlignment" "ListboxTop" "Max" "Min" "Position" "String" "Style" "SliderStep" "TooltipString" "Units" "Value")) ("uimenu" . ("Accelerator" "Callback" "Checked" "Enable" "ForegroundColor" "Label" "Position" "Separator")) ;; Flesh this out more later. ("uipushtool\\|uitoggletool\\|uitoolbar" . ("Cdata" "Callback" "Separator" "Visible")) ) "List of property lists on a per object type basis.") (defvar matlab-unknown-type-commands "[gs]et\\|findobj\\|waitfor" "Expression for commands that have unknown types.") (defun matlab-all-known-properties () "Return a list of all properties." (let ((lst matlab-core-properties) (tl matlab-property-lists)) (while tl (setq lst (append lst (cdr (car tl))) tl (cdr tl))) (matlab-uniquify-list lst))) (defvar matlab-all-known-properties (matlab-all-known-properties) "List of all the known properties.") ;;;###autoload (defmacro matlab-property-function () "Regexp of all builtin functions that take property lists." '(let ((r matlab-unknown-type-commands) (tl matlab-property-lists)) (while tl (setq r (concat r "\\|" (car (car tl))) 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: 1) Assignment 2) if|for|while|switch 3) global variables 4) function arguments. All elements are saved in a list, which is then uniquified. If NEXT is non-nil, then the next element from the saved list is used. If the list is empty, then searches continue backwards through the code." (matlab-navigation-syntax (let* ((bounds (save-excursion (if (re-search-backward "^\\s-*function\\>" nil t) (match-beginning 0) (point-min)))) (syms (append (save-excursion (let ((lst nil)) (while (and (re-search-backward (concat "^\\s-*\\(" prefix "\\w+\\)\\s-*=") bounds t) (< (length lst) 10)) (setq lst (cons (match-string 1) lst))) (nreverse lst))) (save-excursion (let ((lst nil)) (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 (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 "\\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))) (save-excursion (if (and (re-search-backward "^\\s-*function\\>" bounds t) (re-search-forward "\\_<\\(\\w+\\)\\s-*(" (matlab-point-at-eol) t)) (let ((lst nil) m e) (while (looking-at "\\(\\w+\\)\\s-*[,)]\\s-*") (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)))))) (fl nil)) (while syms (if (car syms) (setq fl (cons (car syms) fl))) (setq syms (cdr syms))) (matlab-uniquify-list (nreverse fl))))) (defvar matlab-most-recent-variable-list nil "Maintained by `matlab-find-recent-variable'.") (defun matlab-find-recent-variable (prefix &optional next) "Return the most recently used variable starting with PREFIX as a string. See `matlab-find-recent-variable-list' for details. In NEXT is non-nil, than continue through the list of elements." (if next (let ((next (car matlab-most-recent-variable-list))) (setq matlab-most-recent-variable-list (cdr matlab-most-recent-variable-list)) next) (let ((syms (matlab-find-recent-variable-list prefix)) (first nil)) (if (eq matlab-completion-technique 'complete) syms (setq first (car syms)) (setq matlab-most-recent-variable-list (cdr syms)) first)))) (defun matlab-find-user-functions-list (prefix) "Return a list of user defined functions that match PREFIX." (matlab-navigation-syntax (let ((syms (append (save-excursion (goto-char (point-min)) (let ((lst nil)) (while (re-search-forward "^\\s-*function\\>" nil t) (if (re-search-forward (concat "\\_<\\(" prefix "\\w+\\)\\s-*\\($\\|(\\)") (matlab-point-at-eol) t) (setq lst (cons (match-string 1) lst)))) (nreverse lst))) (let ((lst nil) (files (directory-files default-directory nil (concat "^" prefix "[a-zA-Z][a-zA-Z0-9_]+\\.m$")))) (while files (setq lst (cons (progn (string-match "\\.m" (car files)) (substring (car files) 0 (match-beginning 0))) lst) files (cdr files))) lst))) (fl nil)) (while syms (if (car syms) (setq fl (cons (car syms) fl))) (setq syms (cdr syms))) (matlab-uniquify-list (nreverse fl))))) (defvar matlab-user-function-list nil "Maintained by `matlab-find-user-functions'.") (defun matlab-find-user-functions (prefix &optional next) "Return a user function that match PREFIX and return it. If optional argument NEXT is non-nil, then return the next found object." (if next (let ((next (car matlab-user-function-list))) (setq matlab-user-function-list (cdr matlab-user-function-list)) next) (let ((syms (matlab-find-user-functions-list prefix)) (first nil)) (if (eq matlab-completion-technique 'complete) syms (setq first (car syms)) (setq matlab-user-function-list (cdr syms)) first)))) (defvar matlab-generic-list-placeholder nil "Maintained by `matlab-generic-list-expand'. Holds sub-lists of symbols left to be expanded.") (defun matlab-generic-list-expand (list prefix &optional next) "Return an element from LIST that start with PREFIX. If optional NEXT argument is non nil, then the next element in the list is used. nil is returned if there are not matches." (if next (let ((next (car matlab-generic-list-placeholder))) (setq matlab-generic-list-placeholder (cdr matlab-generic-list-placeholder)) next) (let ((re (concat "^" (regexp-quote prefix))) (first nil) (fl nil)) (while list (if (string-match re (car list)) (setq fl (cons (car list) fl))) (setq list (cdr list))) (setq fl (nreverse fl)) (if (eq matlab-completion-technique 'complete) fl (setq first (car fl)) (setq matlab-generic-list-placeholder (cdr fl)) first)))) (defun matlab-solo-completions (prefix &optional next) "Return PREFIX matching elements for solo symbols. If NEXT then the next patch from the list is used." (matlab-generic-list-expand matlab-keywords-solo prefix next)) (defun matlab-value-completions (prefix &optional next) "Return PREFIX matching elements for value symbols. If NEXT then the next patch from the list is used." (matlab-generic-list-expand matlab-keywords-return prefix next)) (defun matlab-boolean-completions (prefix &optional next) "Return PREFIX matching elements for boolean symbols. If NEXT then the next patch from the list is used." (matlab-generic-list-expand matlab-keywords-boolean prefix next)) (defun matlab-property-completions (prefix &optional next) "Return PREFIX matching elements for property names in strings. If NEXT then the next property from the list is used." (let ((f (matlab-function-called-at-point)) (lst matlab-property-lists) (foundlst nil) (expandto nil)) ;; Look for this function. If it is a known function then we ;; can now use a subset of available properties! (while (and lst (not foundlst)) (if (string= (car (car lst)) f) (setq foundlst (cdr (car lst)))) (setq lst (cdr lst))) (if foundlst (setq foundlst (append foundlst matlab-core-properties)) (setq foundlst matlab-all-known-properties)) (setq expandto (matlab-generic-list-expand foundlst prefix next)) ;; This looks to see if we have a singular completion. If so, ;; then return it, and also append the "'" to the end. (cond ((and (listp expandto) (= (length expandto) 1)) (setq expandto (list (concat (car expandto) "'")))) ((stringp expandto) (setq expandto (concat expandto "'")))) expandto)) (defvar matlab-last-prefix nil "Maintained by `matlab-complete-symbol'. The prefix used for the first completion command.") (defvar matlab-last-semantic nil "Maintained by `matlab-complete-symbol'. The last type of semantic used while completing things.") (defvar matlab-completion-search-state nil "List of searching things we will be doing.") ;;;###autoload (defun matlab-complete-symbol (&optional arg) "Complete a partially typed symbol in a MATLAB mode buffer." (interactive "P") (if (and (featurep 'matlab-shell) (matlab-shell-active-p) matlab-shell-ask-MATLAB-for-completions) ;; Use MATLAB shell if active and asking for completions is enabled. (matlab-complete-symbol-with-shell arg) ;; Else, do the antique version. (matlab-complete-symbol-local arg) )) (defun matlab-complete-symbol-with-shell (&optional arg) "Complete a partially typed symbol in a MATLAB mode buffer using `matlab-shell'. Use `completion-in-region' to support the completion behavior." (interactive "P") ;; Try to do completion with the shell (matlab-navigation-syntax (let* ((common-substr-start-pt nil) (common-substr-end-pt nil) (prefix (if (and (not (eq last-command 'matlab-complete-symbol)) (member (preceding-char) '(? ?\t ?\n ?, ?\( ?\[ ?\'))) "" (buffer-substring-no-properties (save-excursion (forward-word -1) (setq common-substr-start-pt (point))) (setq common-substr-end-pt (point))))) (completion-info (matlab-shell-completion-list prefix)) (completions (cdr (assoc 'completions completion-info))) ) (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' then undo the last completion, and find a new one. The types of symbols tried are based on the semantics of the current cursor position. There are two types of symbols. For example, if the cursor is in an if statement, boolean style functions and symbols are tried first. If the line is blank, then flow control, or high level functions are tried first. The completion technique is controlled with `matlab-completion-technique' It defaults to incremental completion described above. If a completion list is preferred, then change this to 'complete. If you just want a completion list once, then use the universal argument ARG to change it temporarily." (interactive "P") (matlab-navigation-syntax (let* ((prefix (if (and (not (eq last-command 'matlab-complete-symbol)) (member (preceding-char) '(? ?\t ?\n ?, ?\( ?\[ ?\'))) "" (buffer-substring-no-properties (save-excursion (forward-word -1) (point)) (point)))) (sem (matlab-lattr-semantics prefix)) (matlab-completion-technique (if arg (cond ((eq matlab-completion-technique 'complete) 'increment) (t 'complete)) matlab-completion-technique))) (if (not (eq last-command 'matlab-complete-symbol)) (setq matlab-last-prefix prefix matlab-last-semantic sem matlab-completion-search-state (matlab--complete-compute-search-functions sem))) (cond ((eq matlab-completion-technique 'increment) (let ((r nil) (donext (eq last-command 'matlab-complete-symbol))) (while (and (not r) matlab-completion-search-state) (message "Expand with %S" (car matlab-completion-search-state)) (setq r (funcall (car matlab-completion-search-state) matlab-last-prefix donext)) (if (not r) (setq matlab-completion-search-state (cdr matlab-completion-search-state) donext nil))) (delete-region (point) (progn (forward-char (- (length prefix))) (point))) (if r (insert r) (insert matlab-last-prefix) (message "No completions.")))) ((eq matlab-completion-technique 'complete) (let ((allsyms (apply 'append (mapcar (lambda (f) (funcall f prefix)) matlab-completion-search-state)))) (cond ((null allsyms) (message "No completions.") (ding)) ((= (length allsyms) 1) (delete-region (point) (progn (forward-char (- (length prefix))) (point))) (insert (car allsyms))) ((= (length allsyms) 0) (message "No completions.")) (t (let* ((al (mapcar (lambda (a) (list a)) allsyms)) (c (try-completion prefix al))) ;; This completion stuff lets us expand as much as is ;; available to us. When the completion is the prefix ;; then we want to display all the strings we've ;; encountered. (if (and (stringp c) (not (string= prefix c))) (progn (delete-region (point) (progn (forward-char (- (length prefix))) (point))) (insert c)) ;; `display-completion-list' does all the complex ;; ui work for us. (with-output-to-temp-buffer "*Completions*" (display-completion-list (matlab-uniquify-list allsyms))))))))))))) (provide 'matlab-complete) ;;; matlab-complete.el ends here ;; LocalWords: el Ludlam zappo ish defcustom CLim XColor XDir XLabel ;; LocalWords: XAxis XScale YColor YDir YAxis YScale YTick ZColor ZDir ZGrid ;; LocalWords: ZLabel ZScale ZTick Dithermap defun lst tl setq cdr defmacro ;; LocalWords: nreverse eol progn foundlst expandto listp stringp sem lattr ;; LocalWords: donext funcall allsyms mapcar matlab-mode/mlint.el0000664000175000017500000011363414625574731015451 0ustar sebastiensebastien;;; mlint.el --- run mlint in a MATLAB buffer ;; Author: Eric M. Ludlam ;; Maintainer: Eric M. Ludlam ;; Created: June 25, 2002 (defvar mlint-version "1.3.2" "The current version of mlint minor mode.") ;; Copyright (C) 2002-2020 Eric Ludlam ;; ;; 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, 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;; ;; Run mlint, and highlight the problems in the buffer. ;; (eval-and-compile (require 'matlab-compat)) (require 'matlab) (require 'linemark) (eval-when-compile (require 'font-lock) (require 'cl)) (eval-and-compile ;; `object-name-string' is an obsolete function (as of 24.4); use `eieio-object-name-string' instead. (cond ((fboundp 'eieio-object-name-string) (defalias 'mlint-object-name-string 'eieio-object-name-string)) (t (defalias 'mlint-object-name-string 'object-name-string))) ) ;; If we can't find an mlint program this fcn will be needed. (autoload 'matlab-mode-determine-matlabroot "matlab-shell" "\ Return the MATLABROOT for the 'matlab-shell-command'. \(fn)" nil nil) ;; `goto-line' is for interactive use only; use `forward-line' instead. (defun mlint-goto-line (n) (goto-char (point-min)) (forward-line (1- n))) ;;; Code: (defvar mlint-platform ;; See ;; >> lower(computer) ;; MATLABROOT/bin/util/arch.sh (or arch.bat) (cond ((eq system-type 'darwin) (cond ((string-match "^arm" system-configuration) ;; e.g. arm-apple-darwin20.3.0 "maca64") ((string-match "^x86_64" system-configuration) "maci64") ((string-match "^i386" system-configuration) (let ((mt (getenv "MACHTYPE"))) (if (and (stringp mt) (string= "x86_32" mt)) ;; This hack is bad since an Emacs started from ;; the doc doesn't have this variable, thus by defaulting ;; to checking the 32 bit (not common anymore) version, ;; we'll get the right answer most of the time. "maci" "maci64"))) (t "mac"))) ((eq system-type 'gnu/linux) (cond ((string-match "64\\|i686" system-configuration) "glnxa64") (t "glnx86"))) ((eq system-type 'solaris) "sol2") ((eq system-type 'hpux) "hpux") ((eq system-type 'windows-nt) ;; Thought about checking the env PROCESSOR_ARCHITEW6432, ;; but this said AMD on my Intel, which seemed suspicious. (let ((proc (getenv "PROCESSOR_IDENTIFIER"))) (if (and (stringp proc) (string-match "64" proc)) "win64" "win32"))) (t "unknown")) "MATLAB platform we are running mlint on. See >> lower(computer).") (defcustom mlint-calculate-cyclic-complexity-flag nil "*Non-nil means to collect cyclic complexity values." :group 'mlint :type 'boolean) (defvar mlint-symtab-info nil "Symbol Table collected from highlighting cross function variables. Each entry in the symtab is at an index indicating the line it occurs on. Each entry is of the form: ( \"FUNCTION-NAME\" PARENT-IDX ( LINENO . COLNO ) ...) and ... is a list of cross-function variable usages.") (make-variable-buffer-local 'mlint-symtab-info) (defun mlint-programs-set-fcn (&optional symbol value) "The :set function for `matlab-programs'. SYMBOL is the variable being set. VALUE is the new value." (condition-case nil (custom-set-default symbol value) (error (set symbol value))) (mlint-reset-program)) (defvar mlint-program-selection-fcn nil "Function to specify the `mlint-program' for the current buffer. For example, (eval-after-load \"mlint\" '(setq mlint-program-selection-fcn 'my-function-to-select-mlint-program)) will setup `my-function-to-select-mlint-program' to pick the mlint for a buffer. After opening a *.m file, `my-function-to-select-mlint-program' is called and the appropriate mlint should be returned. If there's no mlint program available, nil should be returned and mlint will not be activated.") (defvar mlint-program nil "Program to run for MLint. This value can be automatically set by `mlint-programs'.") (defvar mlint-programs) ;; forward declaration to quiet compiler warning (defun mlint-reset-program () "Reset `mlint-program'." (setq mlint-program (let* ((root (matlab-mode-determine-matlabroot)) (bin (expand-file-name "bin" root)) (mlp mlint-programs) (ans nil)) (while (and mlp (not ans)) (cond ((null (car mlp)) nil) ((file-executable-p (car mlp)) (setq ans (car mlp))) ((executable-find (car mlp)) (setq ans (executable-find (car mlp)))) ;; Use the matlabroot found by matlab-shell ((file-executable-p (expand-file-name (car mlp) bin)) (setq ans (expand-file-name (car mlp) bin))) (t nil)) (setq mlp (cdr mlp))) ans))) (defcustom mlint-programs (list "mlint" (concat mlint-platform "/mlint")) "*List of possible locations of the mlint program." :group 'mlint :type '(repeat (file :tag "MLint Program: ")) :set 'mlint-programs-set-fcn) (defcustom mlint-flags '("-all" "-id") ; "-fix") % Need to support this output "*List of flags passed to mlint." :group 'mlint :type '(repeat (string :tag "Option: "))) (defconst mlint-output-regex "^L \\([0-9]+\\) (C \\([-0-9]+\\)): \\(\\w+\\): \\([^\n]+\\)" "Regular expression for collecting mlint output.") (defconst mlint-symtab-line-regex ;; serial number name parent (concat "^ *\\([0-9]+\\) +\\([a-zA-Z0-9_]+\\) +\\([0-9]+\\)" ;; cross-function variable function-line function-column " +\\(V +CH\\(Set\\|Used\\).*\\|F.* \\([0-9]+\\)/\\([0-9]+\\)\\)$") "Regular expression for mlint symbol table line.") (defcustom mlint-verbose nil "*Non nil if command `mlint-minor-mode' should display messages while running." :group 'mlint :type 'boolean) (defcustom mlint-scan-for-fixes-flag t "Non-nil means that we should scan mlint output for things to fix. Scanning using `mlint-error-fix-alist' can slow things down, and may be cause for being turned off in a buffer." :group 'mlint :type 'boolean) (make-variable-buffer-local 'mlint-scan-for-fixes-flag) (defvar mlint-error-id-fix-alist '( ( AND2 . mlint-lm-entry-logicals) ( OR2 . mlint-lm-entry-logicals) ( INUSD . mlint-lm-entry-unused-argument ) ( 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 created is of the class in CDR.") (defun mlint-column-output (string) "Convert the mlint column output to a cons pair. \(COLSTART . COLEND). Argument STRING is the text to interpret." (save-match-data (if (string-match "\\([0-9]+\\)-\\([0-9]+\\)" string) (cons (string-to-number (match-string 1 string)) (string-to-number (match-string 2 string))) (let ((i (string-to-number string))) (cons i i))))) (defun mlint-run (&optional buffer) "Run mlint on BUFFER and return a list of issues. If BUFFER is nil, use the current buffer." (when (and (file-exists-p (buffer-file-name)) mlint-program) (if (not (file-executable-p mlint-program)) (progn (message "Unable to mlint, %s doesn't exist" mlint-program) (sit-for 2) nil) (let* ((fn (file-name-nondirectory (buffer-file-name (current-buffer)))) (dd default-directory) (buffer-mlint-program mlint-program) (dd default-directory) (show-mlint-warnings matlab-show-mlint-warnings) (highlight-cross-function-variables (and matlab-functions-have-end matlab-highlight-cross-function-variables)) (flags (let ((tmp (if matlab-show-mlint-warnings mlint-flags nil))) (setq tmp (if highlight-cross-function-variables (cons "-edit" tmp) tmp)) (setq tmp (if mlint-calculate-cyclic-complexity-flag (cons "-cyc" tmp) tmp)) tmp)) (errors nil) (n nil) (symtab nil)) (with-current-buffer (get-buffer-create "*M-Lint*") (erase-buffer) (when mlint-verbose (message "Running mlint...")) (setq default-directory dd) (apply 'call-process buffer-mlint-program nil (current-buffer) nil (append flags (list fn))) (when mlint-verbose (message "Running mlint...done")) (goto-char (point-min)) (when highlight-cross-function-variables (when (not (re-search-forward mlint-output-regex nil t)) (goto-char (point-max))) (when (re-search-backward "^ *\\([0-9]+\\)" nil t) (goto-char (point-min)) (setq n (1+ (string-to-number (match-string 1)))) (setq symtab (make-vector n nil)) (while (re-search-forward mlint-symtab-line-regex nil t) (let ((name (match-string 2)) (parent-index (string-to-number (match-string 3))) (column (match-string 7))) (if column ;; line defines a function (aset symtab (string-to-number (match-string 1)) (list name (when (/= parent-index 0) parent-index) (cons (string-to-number (match-string 6)) (string-to-number column)))) (let ((parent (cddr (aref symtab parent-index)))) (if parent (rplacd parent (cons name (cdr parent)))))))))) (when show-mlint-warnings (while (re-search-forward mlint-output-regex nil t) (setq errors (cons (list (string-to-number (match-string 1)) (mlint-column-output (match-string 2)) (match-string 4) "" ; this was the warning code (level) (match-string 3) ) errors)))) ) (mlint-clear-nested-function-info-overlays) (setq mlint-symtab-info symtab) (when (and highlight-cross-function-variables (integerp n)) ;; Then set up new overlays for cross-function variables ;; and nested functions. (save-excursion (while (> n 0) (setq n (1- n)) (let ((entry (aref mlint-symtab-info n))) (if entry (let ((where (caddr entry))) (mlint-goto-line (car where)) (forward-char (1- (cdr where))) (re-search-backward "function\\b") (setq where (point)) (condition-case nil (matlab-forward-sexp) (error (goto-char (point-max)))) (if (cadr entry) ; nested (overlay-put (make-overlay where (point)) 'nested-function t)) (if (cdddr entry) (overlay-put (make-overlay where (point)) 'cross-function-variables (concat "\\b\\(" (mapconcat #'(lambda (x) x) (cdddr entry) "\\|") "\\)\\b"))))))))) errors )))) (defclass mlint-lm-group (linemark-group) () "Group of linemarks for mlint.") (defclass mlint-lm-entry (linemark-entry) ((column :initarg :column :type integer :documentation "The column on which the warning occurs.") (column-end :initarg :column-end :type integer :documentation "The column on which the warning ends.") (coverlay :type overlay :documentation "Overlay used for the specific part of the line at issue.") (warning :initarg :warning :type string :documentation "The error message created by mlint on this line.") (warningid :initarg :warningid :type symbol :documentation "The error id provided by mlint. Warning ID's won't change between releases, unlike the warning messages.") (warningcode :initarg :warningcode :type symbol :initform 'minor :documentation "mlint return code for this type of warning.") (fixable-p :initform nil :allocation :class :type boolean :documentation "Can this class auto-fix the problem?") (fix-description :initform nil :allocation :class :type (or string null) :documentation "Description of how the fix will effect the buffer.") ) "A linemark entry.") (defun mlint-linemark-create-group () "Create a group object for tracking linemark entries. Do not permit multiple groups with the same name." (let* ((name "mlint") (newgroup (mlint-lm-group name :face 'linemark-go-face)) (foundgroup nil) (lmg linemark-groups)) (while (and (not foundgroup) lmg) (if (string= name (mlint-object-name-string (car lmg))) (setq foundgroup (car lmg))) (setq lmg (cdr lmg))) (if foundgroup (setq newgroup foundgroup) (setq linemark-groups (cons newgroup linemark-groups)) newgroup))) (defvar mlint-mark-group (mlint-linemark-create-group) "Group of marked lines for mlint.") (defun mlint-warningid->class (warningid) "For a given WARNINGID, return a class for that warning. Different warnings are handled by different classes." (if mlint-scan-for-fixes-flag (let ((al mlint-error-id-fix-alist)) (while (and al (not (eq (car (car al)) warningid)) ) (setq al (cdr al))) (or (cdr (car al)) 'mlint-lm-entry)) 'mlint-lm-entry)) (cl-defmethod linemark-new-entry ((g mlint-lm-group) &rest args) "Add a `linemark-entry' to G. It will be at location FILE and LINE, and use optional FACE. Call the new entry's activate method. Optional ARGS specifies details about the entry." (let* ((f (plist-get args :filename)) (l (plist-get args :line)) (wc (plist-get args :warningcode)) (c (mlint-warningid->class (plist-get args :warningid))) ) (when (stringp f) (setq f (file-name-nondirectory f))) (apply c (format "%s %d" f l) args) )) (defun mlint-end-of-something () "Move cursor to the end of whatever the cursor is on." (cond ((looking-at "\\w\\|\\s(") (forward-sexp 1)) ((looking-at "\\s.") (skip-syntax-forward ".")) (t (error nil)))) (defvar mlint-overlay-map) ;; quiet compiler warning with forward declaration (cl-defmethod linemark-display ((e mlint-lm-entry) active-p) "Set object E to be active. ACTIVE-P if it should be made visible." ;; A bug in linemark prevents individual entry colors. ;; Fix the color here. (let ((wc (oref e warningcode))) (oset e :face (cond ((eq wc 'major) 'linemark-stop-face) ((eq wc 'medium) 'linemark-caution-face) (t 'linemark-go-face)))) ;; Call our parent method (cl-call-next-method) ;; Add highlight area (if active-p (when (and (not (slot-boundp e 'coverlay)) (slot-boundp e 'overlay) (oref e overlay)) (with-slots (overlay column column-end warning) e (let ((warntxt (if (mlint-is-fixable e) (concat warning "\nC-c , f to " (oref e fix-description)) warning))) ;; We called super first, so this should be an active overlay. (overlay-put overlay 'local-map mlint-overlay-map) (overlay-put overlay 'help-echo warntxt) ;; Now, if we have some column data, lets put more highlighting on. (with-current-buffer (overlay-buffer overlay) (goto-char (overlay-start overlay)) (condition-case nil (forward-char (1- column)) ;;(move-to-column (1- column)) (error nil)) (oset e coverlay (make-overlay (point) (progn (beginning-of-line) (forward-char column-end) ;(move-to-column column-end) (point)) (current-buffer))) (with-slots (coverlay) e (overlay-put coverlay 'face 'linemark-funny-face) (overlay-put coverlay 'obj e) (overlay-put coverlay 'tag 'mlint) (overlay-put coverlay 'help-echo warntxt) ) )))) ;; Delete our spare overlay (when (slot-boundp e 'coverlay) (with-slots (coverlay) e (when coverlay (condition-case nil (delete-overlay coverlay) (error nil)) (slot-makeunbound e 'coverlay))) ))) (cl-defmethod mlint-is-fixable ((e mlint-lm-entry)) "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)) "Stuff to do after a warning is considered fixed. Subclasses fulfill the duty of actually fixing the code." (linemark-display e nil) (linemark-delete e)) (cl-defmethod mlint-fix-entry ((e mlint-lm-entry)) "This entry E cannot fix warnings, so throw an error. Subclasses fulfill the duty of actually fixing the code." (error "Don't know how to fix warning")) ;;; Specialized classes ;; (defclass mlint-lm-delete-focus (mlint-lm-entry) ((fixable-p :initform t) (fix-description :initform "Delete the offending characters.") ) "Specialized entry for deleting the highlighted entry.") (cl-defmethod mlint-fix-entry ((ent mlint-lm-delete-focus)) "Add semi-colon to end of this line ENT." (save-excursion (mlint-goto-line (oref ent line)) (let* ((s (progn (move-to-column (1- (oref ent column))) (point))) (e (progn (move-to-column (oref ent column-end)) (point))) ) (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) )) ) (defclass mlint-lm-replace-focus (mlint-lm-delete-focus) ((fix-description :initform "Replace the offending symbol with ") (new-text :initform "") ) "Class which can replace the focus area." :abstract t) (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 this fix-description) (oref this new-text)))) (cl-defmethod mlint-fix-entry ((ent mlint-lm-replace-focus)) "Replace the focus area with :new-text." (let ((pos (cl-call-next-method))) (save-excursion (goto-char (point)) (insert (oref ent new-text))))) (defclass mlint-lm-str2num (mlint-lm-replace-focus) ((new-text :initform "str2double")) "Replace str2num with str2double") (defclass mlint-lm-entry-deprecated (mlint-lm-replace-focus) () "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) &rest fields) "Calculate the 'new text' for THIS instance. Optional argument FIELDS are the initialization arguments." ;; After basic initialization, update the new text field. (let* ((warn (oref this warning)) (junk (string-match "Use \\(\\w+\\) instead" warn)) (newfcn (when junk (downcase (substring warn (match-beginning 1) (match-end 1)))))) (oset this new-text newfcn) ;; After basic initialization, update the fix description. (oset this 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.") ) "Specialized logical and/or class.") (cl-defmethod mlint-fix-entry ((ent mlint-lm-entry-logicals)) "Replace the single logical with double logical." (save-excursion (mlint-goto-line (oref ent line)) (let* ((s (progn (move-to-column (1- (oref ent column))) (point))) (e (progn (move-to-column (oref ent column-end)) (point))) (txt (buffer-substring-no-properties s e))) (goto-char s) ;; All of these are replacing single logicals with double. (insert txt))) ) (defclass mlint-lm-entry-unused-argument (mlint-lm-entry) ((fixable-p :initform t) (fix-description :initform "remove this argument.") ) "Specialized logical and/or class.") (cl-defmethod mlint-fix-entry ((ent mlint-lm-entry-unused-argument)) "Remove the arguments." (save-excursion (mlint-goto-line (oref ent line)) (let* ((s (progn (move-to-column (1- (oref ent column))) (point))) (e (progn (move-to-column (oref ent column-end)) (point))) ) (goto-char s) (if (not (looking-at "(\\|,")) (forward-char -1)) (delete-region (point) e) )) ) (defclass mlint-lm-quiet (mlint-lm-entry) ((fixable-p :initform t) (fix-description :initform "Make sure this line prints no values.") ) "Specialized logical and/or class.") (cl-defmethod mlint-fix-entry ((ent mlint-lm-quiet)) "Add semi-colon to end of this line." (save-excursion (matlab-end-of-command) (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) "Setup ERR, an mlint message to be marked." (save-excursion (linemark-add-entry mlint-mark-group :line (nth 0 err) :column (car (nth 1 err)) :column-end (cdr (nth 1 err)) :warning (nth 2 err) ;; Old style did this lookup, but new versions of ;; MLint replace the warning code with a warning ;; ID which can instead be used for auto-fix. In addition, ;; just use the default warning code. ;;:warningcode 'minor ;; (cdr (assoc (nth 3 err) mlint-warningcode-alist)) :warningid (intern (nth 4 err)) ))) (defun mlint-clear-warnings () "Unhighlight all existing mlint warnings." (interactive) (mapc (lambda (e) (if (string= (oref e filename) (buffer-file-name)) (linemark-delete e))) (oref mlint-mark-group marks))) (defun mlint-clear-nested-function-info-overlays () "Clear out any previous overlays with nested function information. This includes nested-function and cross-function-variables." (let ((overlays (overlays-in (point-min) (point-max)))) (while overlays (let* ((overlay (car overlays))) (if (or (overlay-get overlay 'cross-function-variables) (overlay-get overlay 'nested-function)) (delete-overlay overlay))) (setq overlays (cdr overlays))))) (defun mlint-clear-cross-function-variable-highlighting () "Remove cross-function-variable overlays and re-fontify buffer." (mlint-clear-nested-function-info-overlays) (if (and (boundp 'global-font-lock-mode) global-font-lock-mode (boundp 'font-lock-flush) (not font-lock-mode)) (font-lock-flush (point-min) (point-max)))) (defun mlint-buffer () "Run mlint on the current buffer. Highlight problems and/or cross-function variables." (interactive) (when (and (buffer-file-name) mlint-program) ;; If buffer-file-truename is nil, then it's not safe to save the ;; buffer. magit pulls in the code into a buffer and saving during ;; a magit ediff will result in backing out changes. (if (and buffer-file-truename (buffer-modified-p) (y-or-n-p (format "Save %s before linting? " (file-name-nondirectory (buffer-file-name))))) (save-buffer)) (let ((errs (mlint-run)) ) (mlint-clear-warnings) (while errs (mlint-highlight (car errs)) (setq errs (cdr errs)))))) (defun mlint-next-buffer () "Move to the next warning in this buffer." (interactive) (let ((n (linemark-next-in-buffer mlint-mark-group 1 t))) (if n (progn (mlint-goto-line (oref n line)) (message (oref n warning))) (ding)))) (defun mlint-prev-buffer () "Move to the prev warning in this buffer." (interactive) (let ((n (linemark-next-in-buffer mlint-mark-group -1 t))) (if n (progn (mlint-goto-line (oref n line)) (message (oref n warning))) (ding)))) (defun mlint-next-buffer-new () "Move to the next new warning in this buffer." (interactive) (let ((p (linemark-at-point (point) mlint-mark-group)) (n (linemark-next-in-buffer mlint-mark-group 1 t))) ;; Skip over messages that are the same as the one under point. (save-excursion (while (and n p (not (eq n p)) (string= (oref p warning) (oref n warning))) (mlint-goto-line (oref n line)) (setq n (linemark-next-in-buffer mlint-mark-group 1 t)))) (if n (progn (mlint-goto-line (oref n line)) (message (oref n warning))) (ding)))) (defun mlint-prev-buffer-new () "Move to the prev new warning in this buffer." (interactive) (let ((p (linemark-at-point (point) mlint-mark-group)) (n (linemark-next-in-buffer mlint-mark-group -1 t))) ;; Skip over messages that are the same as the one under point. (save-excursion (while (and n p (not (eq n p)) (string= (oref p warning) (oref n warning))) (mlint-goto-line (oref n line)) (setq n (linemark-next-in-buffer mlint-mark-group -1 t)))) (if n (progn (mlint-goto-line (oref n line)) (message (oref n warning))) (ding)))) (defun mlint-show-warning () "Show the warning for the current mark." (interactive) (let ((n (linemark-at-point (point) mlint-mark-group))) (if (not n) (message "No warning at point.") (message (oref n warning))))) (defun mlint-fix-warning () "Show the warning for the current mark." (interactive) (let ((n (linemark-at-point (point) mlint-mark-group))) (if (not n) (message "No warning at point.") (if (not (mlint-is-fixable n)) (message "No method for fixing this warning.") (mlint-fix-entry n))))) (defun mlint-mark-ok () "Mark this line as M-Lint Ok." (interactive) (let ((n (linemark-at-point (point) mlint-mark-group))) (if (not n) (message "No warning at point.") (let ((col (matlab-comment-on-line))) (if col (progn (goto-char col) (skip-chars-forward "% ") (insert "#ok ")) (end-of-line) (insert " %#ok")) ) ;; This causes inconsistencies. ;; (linemark-delete n) )) ) ;;; Define an mlinting minor mode ;; (defvar mlint-minor-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c,n" 'mlint-next-buffer) (define-key map "\C-c,p" 'mlint-prev-buffer) (define-key map "\C-c,N" 'mlint-next-buffer-new) (define-key map "\C-c,P" 'mlint-prev-buffer-new) (define-key map "\C-c,g" 'mlint-buffer) (define-key map "\C-c,c" 'mlint-clear-warnings) (define-key map "\C-c, " 'mlint-show-warning) (define-key map "\C-c,f" 'mlint-fix-warning) (define-key map "\C-c,o" 'mlint-mark-ok) map) "Minor mode keymap used when mlinting a buffer.") (easy-menu-define mlint-minor-menu mlint-minor-mode-map "M-Lint Minor Mode Menu" '("M-Lint" ["Get M-Lint Warnings" mlint-buffer t] ["Clear M-Lint Warnings" mlint-clear-warnings t] ["Show Warning" mlint-show-warning (linemark-at-point (point) mlint-mark-group)] ["Auto Fix Warning" mlint-fix-warning (let ((w (linemark-at-point (point) mlint-mark-group))) (and mlint-scan-for-fixes-flag w (mlint-is-fixable w))) ] ["Enable Auto-fix scanning" (setq mlint-scan-for-fixes-flag (not mlint-scan-for-fixes-flag)) :style toggle :selected mlint-scan-for-fixes-flag ] ["This is Ok" mlint-mark-ok (linemark-at-point (point) mlint-mark-group) ] "--" ["Next Warning" mlint-next-buffer t] ["Previous Warning" mlint-prev-buffer t] ["Next New Warning" mlint-next-buffer-new t] ["Previous New Warning" mlint-prev-buffer-new t] )) (defvar mlint-overlay-map (let ((map (make-sparse-keymap ))) (define-key map [down-mouse-3] 'mlint-emacs-popup-kludge) (define-key map [(meta n)] 'mlint-next-buffer) (define-key map [(meta p)] 'mlint-prev-buffer) (define-key map [(control meta n)] 'mlint-next-buffer-new) (define-key map [(control meta p)] 'mlint-prev-buffer-new) (set-keymap-parent map matlab-mode-map) map) "Map used in overlays marking mlint warnings.") (defun mlint-emacs-popup-kludge (e) "Pop up a menu related to the clicked on item. Must be bound to event E." (interactive "e") (let ((repos nil) (ipos nil) (startpos (point)) ) (save-excursion (mouse-set-point e) (setq ipos (point)) (popup-menu mlint-minor-menu) (if (/= (point) ipos) (setq repos (point))) ) (when repos (goto-char repos)))) ;;;###autoload (easy-mmode-define-minor-mode mlint-minor-mode "Toggle mlint minor mode, a mode for showing mlint errors. With prefix ARG, turn mlint minor mode on iff ARG is positive. \\{mlint-minor-mode-map\\}" nil " mlint" mlint-minor-mode-map (if (and mlint-minor-mode (not (eq major-mode 'matlab-mode))) (progn (mlint-minor-mode -1) (error "M-Lint minor mode is only for MATLAB Major mode"))) (if (not mlint-minor-mode) (progn (mlint-clear-nested-function-info-overlays) (mlint-clear-warnings) (remove-hook 'after-save-hook 'mlint-buffer t) (easy-menu-remove mlint-minor-menu) ) ;; activate mlint if possible (if mlint-program-selection-fcn (let ((ans (funcall mlint-program-selection-fcn))) (when ans (make-local-variable 'mlint-program) (setq mlint-program ans))) ;; else use global mlint-program for all *.m files (if (not mlint-program) (if (y-or-n-p "No MLINT program available. Configure it? ") (customize-variable 'mlint-programs)))) (if mlint-program (progn (add-hook 'after-save-hook 'mlint-buffer nil t) (easy-menu-add mlint-minor-menu mlint-minor-mode-map) (mlint-buffer)) ;; Remove the mlint menu. set mlint-minor-mode variable to nil, disable mlint keybindings (mlint-minor-mode -1)))) (defvar mlint-minor-mode-was-enabled-before nil "Non nil if mlint is off, and it was auto-disabled.") (make-variable-buffer-local 'mlint-minor-mode-was-enabled-before) (defun mlint-ediff-metabuffer-setup-hook () "Hook run when EDiff is about to do stuff to a buffer. That buffer will be current." (when (and (eq major-mode 'matlab-mode) mlint-minor-mode) (setq mlint-minor-mode-was-enabled-before mlint-minor-mode) (mlint-minor-mode -1) )) (add-hook 'ediff-prepare-buffer-hook 'mlint-ediff-metabuffer-setup-hook) (defun mlint-ediff-cleanup-hook () "Re-enable mlint for buffers being ediffed. The buffer that was originally \"setup\" is not current, so we need to find it." (mapcar (lambda (b) (when (with-current-buffer b (and (eq major-mode 'matlab-mode) mlint-minor-mode-was-enabled-before)) (with-current-buffer b (mlint-minor-mode 1) (setq mlint-minor-mode-was-enabled-before nil)))) (buffer-list))) (add-hook 'ediff-cleanup-hook 'mlint-ediff-cleanup-hook) (provide 'mlint) ;;; mlint.el ends here ;; LocalWords: el Ludlam eludlam compat linemark eieio fboundp defalias fn defun MACHTYPE stringp ;; LocalWords: glnx hpux nt ARCHITEW defcustom symtab COLNO setq MLint mlp cdr defconst alist lm ;; LocalWords: NOPRT NOSEM NOCOM FDEPR COLSTART COLEND cyc aset cddr rplacd integerp caddr sexp ;; LocalWords: cadr cdddr mapconcat defclass linemarks initarg coverlay warningid ID's warningcode ;; LocalWords: initform newgroup foundgroup lmg defmethod plist nondirectory oref oset boundp EDiff ;; LocalWords: warntxt progn makeunbound ent newfcn downcase mapc fontify truename magit ediff prev ;; LocalWords: mlinting keymap repos ipos startpos mmode funcall metabuffer ediffed mapcar matlab-mode/README.org0000664000175000017500000001123714611713120015424 0ustar sebastiensebastien* Matlab-emacs Project: MATLAB and Emacs integration: 1. matlab-mode for editing *.m files. 2. matlab-shell for running MATLAB -nodesktop within Emacs (Unix-only). - matlab-shell will use company-mode for completions. 3. tlc-mode for editing *.tlc files (part of Simulink Coder). 4. Integration with CEDET. ** INSTALL To install directly from the repository, : cd /path/to/matlab-emacs : make Next, add the following to your ~/.emacs file: #+BEGIN_SRC elisp ;; Replace path below to be where your matlab.el file is. (add-to-list 'load-path "/path/to/matlab-emacs") (load-library "matlab-load") ;; Enable CEDET feature support for MATLAB code. (Optional) ;; (matlab-cedet-setup) #+END_SRC ** MELPA GNU emacs (sorry Xemacs folks) users (version >=24) can use the package as provided by MELPA. Set #+BEGIN_SRC elisp (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t) #+END_SRC And then run M-x package-list-packages ** Releases The most recent version of matlab.el is in SourceForge GIT. Homepage: http://matlab-emacs.sf.net Project Page: http://sourceforge.net/projects/matlab-emacs GIT Repository: https://sourceforge.net/p/matlab-emacs/src/ci/master/tree/ You can get a zip file using the "Download snapshot" button or use git to create a local repository: #+BEGIN_SRC shell git clone git://git.code.sf.net/p/matlab-emacs/src matlab-emacs-src #+END_SRC If you do not have a GIT client on your machine, you can use the MATLAB script dl_emacs_support.m to download a fresh copy of the matlab.el sources. https://sourceforge.net/p/matlab-emacs/src/ci/master/tree/dl_emacs_support.m Older versions in the SourceForge CVS repository are *no* longer updated. ** Dependencies MATLAB-Emacs can use the CEDET suite for the following items: - Modifying the build system (Makefiles, etc) - mlint (uses EIEIO object system) - parsing/completion (uses semantic parsing system) - some template insertion features As of Emacs 23.2, CEDET is included in Emacs, and nothing extra is needed to satisfy that dependency. For older versions of Emacs: See http://cedet.sf.net for downloading CEDET. If you only want to use matlab.el for editing code or running the MATLAB shell, you DO NOT need to install CEDET. If you do want to use CEDET, add the following to your .emacs file: #+BEGIN_SRC elisp (matlab-cedet-setup) #+END_SRC ** Mailing List Subscribe to matlab-emacs-discuss mailing list get updates on new releases and discuss topics relevant to the matlab-emacs project. http://lists.sourceforge.net/mailman/listinfo/matlab-emacs-discuss *Old mailing list*: The original mailing list where beta versions of matlab.el were posted, and where comments, questions, bug reports, and answers to questions could be sent. If you were subscribed to this list, please unsubscribe, and subscribe to the new list above. To unsubscribe, send email with the body of: "unsubscribe matlab-emacs" ** FAQ *** How Do I Customize matlab-emacs? You can configure matlab-emacs using the "matlab" or "matlab-shell" customization groups: : Emacs -> Options -> Customize Emacs -> Specific Group *** How do I customize "edit file.m" behavior? By default when you run : M-x matlab-shell : : >> edit file.m file.m will open in emacs using 'emacsclient -n'. matlab-shell achieve this behavior by instructing MATLAB to use 'emacsclient -n' as the external text editor. You can customize this by setting `matlab-shell-emacsclient-command' (Matlab Shell Emacsclient Command) of the matlab-shell customization group. You can change this command to what's appropriate. If you set it to the empty string, 'edit file.m' will use the default MATLAB editor setting. The default MATLAB editor setting is controlled in the MATLAB preferences, (e.g. R2018a Home tab, Environment section, Preferences) where you can select which editor you want to edit a text file. MATLAB Editor or an external text editor. If you always want to use Emacs as your matlab editor even when running MATLAB outside of emacs, select Text editor and set it to the appropriate 'emacsclient -n' command. *** Can I debug *.m files using Emacs as a debugger? Starting with MATLAB 8.5 (R2015b), Emacs can no longer be used as a debugger for debugging MATLAB code. With R2015b, MATLAB no longer provides necessary file and line number information for external debuggers. Therefore, in matlab-shell: : >> dbstop in foo will open foo in the MATLAB editor for debugging. #+STARTUP: showall matlab-mode/tests/0000775000175000017500000000000014611713120015114 5ustar sebastiensebastienmatlab-mode/tests/syntaxerr.m0000664000175000017500000000145514611713120017336 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function syntaxerr() % This function has a syntax error in it. % A = 'incomplete string % A = 1:B B = A(1 end matlab-mode/tests/metest.sh0000775000175000017500000000143014611713120016752 0ustar sebastiensebastien# Copyright (C) 2023 Eric Ludlam (and others) # 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 . #!/bin/bash # # Run a suite of matlab-emacs tests emacs -batch -l metest.el -e "metest-all-syntax-tests" #end matlab-mode/tests/mclass.m0000664000175000017500000001053414611713120016557 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . % >>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 % >>12 methods % !!8 % >>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# % !!12 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 end % <<12 methods (Access='public') % >>13 function meth(obj) % #3# % >>14 if obj.AP % #3# disp('display the word end here'); % #2# else % >>15 try % comment with if, while, parfor words in it. catch % comment with end in it. end % <<15 end % <<14 end % <<13 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 matlab-mode/tests/mfuncnoend.m0000664000175000017500000000154314611713120017431 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/indents.m0000664000175000017500000002026114611713120016737 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function indents(a,b,stuff,cmddual1fake,cmddual2fake) % Help text % !!0 % of many lines % !!0 % including a gap % !!0 arguments (Repeating) % !!4 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 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(cmddual1fake,cmddual2fake); array_constant_decls(); % !!4 continuations_and_block_comments(); % $$$ !!0 % $$$ special ignore comments has_nested_fcn(); % !!4 % !!4 - after ignore comments end % Comment with end in it %!!0 function B = ends_in_comments_and_strings() % !!0 % >>6 if foo A = 1; end % <<6 end in comment after end % !!4 symbol_with_end_in_it; B = A(1:end); %#ok %% cell start comment !!4 if foo %!!4 C = "this is the end of the line"; % !!8 else %!!4 % !!8 end; D = "string end string"; % !!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; end ... the other end % !! 4 B = [ B A ]; % !!4 str = 'This is a char array with ... in it'; foo(str); % !!4 fcncall(arg1, '...', arg3); % !!4 1; % !!4 % Multi- end s % >>8 if foo %#ok if bar %#ok if baz A = B; else end; end; end % <<8 comment end thing % !!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(varargin) % !!0 C = 0; if varargin{1} % if true % !!8 else % !!4 % !!8 end % if true % see previous function % !!4 for x=1:length(C) % !!4 if varargin{2} % !!8 continue % !!12 end % !!8 break % !!8 % !!14 %!!8 end switch foo() %!!4 case 1 %!!6 %!!8 otherwise %!!6 %!!8 end %!!4 try % !!8 catch %!!4 % !!8 end end function B = continuations_and_block_comments % !!0 % !!0 % !!0 %{ !!2 { } !!2 %} arg1=1; %{ % !!4 !!6 % !!4 %} % Block comment indicators MUST be on a line by themselves. %{ Not a block comment } foo(1); % !!4 - don't indent this special %} Not an end to a block comment { foo(arg1, ... %!!4 arg2); %!!8 foo_long_fcn(arg1, ... %!!4 arg2); %!!17 A = [ 1 2 % !!4 3 4 ]; % !!10 foo(['this is a very long string' ... %!!4 'with a continution to do something very exciting']);%!!9 set(gcf,'Position',[ 1 2 3 4], ... !!4 'Color', 'red'); % !!12 B = A + 1 + 4 ... + 6; % !!8 foo_code(); % eol-comment !!4 % 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 function has_nested_fcn plot(1:10); %!!4 A = 1; function am_nested_fcn() %!!4 % help % !!4 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 matlab-mode/tests/stringtest.m0000664000175000017500000001247214611713120017506 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . %% Tests for char vector and string handling. % % #c# % % %%%script script script %% Basic strings 'char vector #v#' "string #s#" charvec = 'character vector #v#'; 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# % Comment with 'char vector incomplete #c# % Comment with "string scalar incomplete #c# %% Strings in Strings charvs = 'char vector with "string #v#" in it'; 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# %% 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# charvbc = 'char vector with %{ comment char #v# %} '; stringbc = "string scalar with %{ comment char #s# %} "; charvel = 'char vector with elipsis ... #v# '; stringel = "string scalar with elipsis ... #s#"; %% Mixed String Char on the same line charv2str = { 'char vec #v#' "string #s#" }; str2charv = { "string #s#" 'char vec #v#' }; cv2s_quoted = { 'char vec and ''quote #v#' "string and "" quote #s#" }; s2cv_quoted = { "string and "" quote #s#" 'char vec and '' quote #v#' }; cv2s_nested = { 'char vec and " quote #v#' "string and ' quote #s#" }; s2cv_nested = { "string and ' quote #s#" 'char vec and " quote #v#' }; cv2s_transp = { 'char vec and t" quote #v#' "string and t' quote #s#" }; s2cv_transp = { "string and t' quote #s#" 'char vec and t" quote #v#' }; cell_in_strs = { "strinc { innercel #s# }" 'charv {innercel #v#}' }; cell_in_strs1_nested = { "strinc { innercel ' #s# }" 'charv { innercell " #v# }' }; cell_in_strs2_nested = { 'charv { innercell " #v# }' "strinc { innercel ' #s# }" }; icell_in_strs1_nested = { "strinc innercel ' #s# }" 'charv innercell " #v# }' }; 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# %% 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, ... #e# ' ''-goo'', ... #v#', newline, ... #e# ' ''-bar'', ... #v#', newline ]; func_call1('function with charvec', ... #e# 'after ellipsis charvec with ellipsis ... #v#'); func_call2('test indendation here. Should not indent'); %% Indentation protection & confusing cell/string mixing icC = 'charv with { in it #v#'; icS = "strings with { in it #s#"; imC = 'charv with [ in it #v#'; imS = "strings with [ in it #s#"; cmC = { 'a b } #v#', 1}; cmS = { "a b } #s#", 2}; %% Concatenation CA = [ 'char' 'vector' 'concat #v#' ]; CAE = [ 'char' 'vect #v#' 'conc' ]; SA = [ "array" "of" "scalar" "strings" "#s#" ]; SAE = [ "vert" "array #s#" "of" "strings" ]; %% Tests for transpose A = [1 2 3]'; B = A'; C = A''; D = { 'cell' 'transpose' '#v#' }'; E = [ 1 2 3 ]''; F = { 'cell' 'trnspose' }''; G = [ "string" "array" "transpose" "#s#" ]'; H = A.'; I = A.''; J = A(B')'; K = 12'; L = "string transpose #s#"'; % Comment with transpose' in it. #c# % Comment with something" in it. #c# %% Unreachable if 0 % Note: unreachable code doesn't break char vectors or strings. Ac = 'char vector #v#'; Bs = "string scalar #s#"; else Cs = "not unreachable #s#"; end %% Block Comments #C# %{ Block Comment: #b# 'char vector #b#' "string scalar #b#" %} 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 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 matlab-mode/tests/buggy.m0000664000175000017500000000276314611713120016417 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function buggy(input,n) % The purpose of this function is to have bugs for creating errors % tha matlab-shell needs to detect. switch input case 'err' error('You encounered an error in buggy.m'); case 'cmderr' % there is no blarg, so this should error. ls blarg case 'warn' warning('You enountered a warning in buggy.m'); case 'stack' if nargin == 1 buggy('stack',3); elseif n == 1 buggy('err'); else buggy('stack',n-1); end case 'indented' % Test errors that are captured, but then reported up % but indented. disp(' In buggy.m (TestPoint_foo) at 36') end end function TestPoint_foo message('A test point'); end matlab-mode/tests/Makefile0000664000175000017500000000260414611713120016556 0ustar sebastiensebastien# Automatically Generated Makefile by EDE. # For use with: make # Relative File Name: tests/Makefile # # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST. # EDE is the Emacs Development Environment. # http://cedet.sourceforge.net/ede.shtml # top=../ ede_FILES=Project.ede Makefile tests_LISP=mstest.el metest.el EMACS=emacs EMACSFLAGS=-batch --no-site-file --eval '(setq debug-on-error t)' require=$(foreach r,$(1),(require (quote $(r)))) LOADPATH= ../ matlab_MISC=buggy.m dbtester.m expressions.m indents.m mclass.m mpclass.m stringtest.m syntaxerr.m cellscript.m fontlock.m testeeval.m 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 $(EMACS) -batch -q -l metest.elc -e "metest-all-syntax-tests" .PHONY: shelltests shelltests: mstest.elc $(EMACS) -batch -q -l mstest.elc -e "mstest-run-all-tests" %.elc: %.el $(EMACS) $(EMACSFLAGS) $(addprefix -L ,$(LOADPATH)) --eval '(progn $(call require, $(PRELOADS)))' -f batch-byte-compile $^ .PHONY: tests tests: $(addsuffix c, $(tests_LISP)) matlab: @ tags: clean: rm -f *.elc .PHONY: dist dist: mkdir $(DISTDIR) cp $(tests_LISP) $(matlab_MISC) $(ede_FILES) $(DISTDIR) # End of Makefile matlab-mode/tests/fontlock.m0000664000175000017500000000722714611713120017121 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . % 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; r = input3; function nested(ni1) % Nest function comment b = ni1; a = q; 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. %% Tests for syntactic expressions in the Emacs sense % %% In comments - just words % % word1 ( list two ) word #4# % % A = 'one charvec'; % #2# B = "one string"; % #2# % >>1 if expr1 a = 'charvec'; % #2# b = "string"; % comment with a few words c = { 1 2 'cell array' }; % #2# end % <<1 cmC = { 'a b } #v#', 1}; % #2# cmS = { "a b } #s#", 2}; % #2# cmCr = { 'a b { #v#', 1}; % #2# cmSr = { "a b { #s#", 2}; % #2# func_A('charvec with ) in it'); % #2# func_A('charvec with ( in it'); % #2# func_B("string with ) in it"); % #2# func_V("string with ( in it"); % #2# mA = [ 'concat charvec' 'with [ in it' ]; % #2# mB = [ 'concat charvec' 'with ] in it' ]; % #2# msA = [ "concat strings" "with [ in it" ]; % #2# msB = [ "concat strings" "with ] in it" ]; % #2# % >>2 if expr2 ifcmC = { 'a b } #v#', 1}; % #2# ficmS = { "a b } #s#", 2}; % #2# else ifcmCr = { 'a b { #v#', 1}; % #2# ifcmSr = { "a b { #s#", 2}; % #2# end % <<2 BM = [ 1 2 3 ... comment 4 5 6 ... comment #4# 7 8 9 ]; CC = { 1 2 { 3 4 } 5 6 { 7 { 8 { 9 }} 10 }}; % #2# CM = { [ 1 2 ] { [ 3 4 ] 5 } [ 6 7 ] A(1:4) }; % #2# AA = []; % >>4 for flv=1:10 if i == 1 AA = [ AA 1 2 3 ]; %#ok - pragma test else AB = AA(2:end); end end % <<4 % >>5 switch AA case 1 % >>51 while false if false end end % <<51 case 2 % >>52 for i=1:10 AC = AA(end:-1:1); AD = pi + nan + true; end % <<52 case 3 % >>53 try error('Moose'); catch E end % <<53 otherwise end % <<5 % end matlab-mode/tests/metest.el0000664000175000017500000005766014611713120016755 0ustar sebastiensebastien;;; metest.el --- Testing suite for MATLaB Emacs ;; ;; Copyright (C) 2019-2023 Eric Ludlam ;; ;; 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 http://www.gnu.org/licenses/. ;;; Commentary: ;; ;; Suite of unit tests that load in demo .m files and verify operation. ;;; Code: (let* ((lf (or load-file-name (buffer-file-name (current-buffer)))) (d1 (file-name-directory lf)) (d (file-name-directory (directory-file-name d1))) ) (defvar met-testfile-path d1 "Location of test MATLAB code.") (add-to-list 'load-path (expand-file-name d) t)) (defvar met-testfile-path) ; quiet compiler (require 'matlab-load) (require 'matlab) (require 'cedet-matlab) (require 'matlab-complete) (require 'semantic-matlab) ;; Enable semantic (semantic-mode 1) (matlab-cedet-setup) (defun metest-all-syntax-tests () "Run all the syntax tests in this file." (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.") (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." (let ((buf (metest-find-file F)) (cnt 0) (noninteractive nil) ;; fake out font lock ) (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.")) ) ;;(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) (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= "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)) ) (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)) (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)) (list cnt "tests"))) (defvar met-sexptest-files '("expressions.m" "mclass.m" "blocks.m") "List of files for running syntactic expression tests.") (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)) (while (re-search-forward "#\\([0-9]\\)#" nil t) (save-excursion (goto-char (match-beginning 0)) (skip-chars-backward " %") ; skip comment part (let* ((num (string-to-number (match-string 1)))) (save-restriction (narrow-to-region (point-at-bol) (point)) (metest-condition-case-error-msg (matlab-move-simple-sexp-internal (- num))) (skip-chars-backward " \t;.=%") (if (not (eq (point) (point-min))) (save-restriction (widen) (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) (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) (list cnt "tests"))) (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." (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)) (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)) (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) (metest-error "Failed to reverse navigate sexp for %d" num)) ) (end-of-line) (setq cnt (1+ cnt)))) (kill-buffer buf) (list cnt "test"))) (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-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)) (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.") (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." (let ((buf (metest-find-file F)) exp act (cnt 0)) (with-current-buffer buf ;; Prep buffer for test (semantic-idle-scheduler-mode -1) (semantic-clear-toplevel-cache) ;; Do the test (goto-char (point-min)) ;;(message ">> Starting semantic parser test in %S" (current-buffer)) (unless (re-search-forward "^%%\\s-*>>\\s-+SEMANTIC TEST" nil t) (metest-error "Semantic parser test: Failed to find test cookie.")) (unless (re-search-forward "^%{[ \t\n]+\\(((\\)" nil t) (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) (match-beginning 0))))) (setq act (semantic-fetch-tags)) ;; Compare the two lists ... simply. (while (and exp act) (unless (metest-compare-tags (car exp) (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) (metest-error "Found tags and expected tag lists differnet lengths.\nExpected Remains: %S\nActual Remains: %S" exp act)) ) (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 matlab-mode/tests/Project.ede0000664000175000017500000000342514611713120017205 0ustar sebastiensebastien;; Copyright (C) 2023 Eric Ludlam (and others) ;; 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 . ;; Object tests ;; EDE Project Files are auto generated: Do Not Edit (ede-proj-project "tests" :file "Project.ede" :name "tests" :targets (list (ede-proj-target-elisp "tests" :object-name "tests" :name "tests" :path "" :source '("mstest.el" "metest.el") :rules (list (ede-makefile-rule "ede-makefile-rule-1578155c1544" :target "modetests" :dependencies "metest.elc" :rules '("$(EMACS) -batch -q -l metest.elc -e \"metest-all-syntax-tests\"") :phony t) (ede-makefile-rule "ede-makefile-rule-157814fbd3f8" :target "shelltests" :dependencies "mstest.elc" :rules '("$(EMACS) -batch -q -l mstest.elc -e \"mstest-run-all-tests\"") :phony t)) :aux-packages '("matlab-load")) (ede-proj-target-makefile-miscelaneous "matlab" :object-name "matlab" :name "matlab" :path "" :source '("buggy.m" "dbtester.m" "expressions.m" "indents.m" "mclass.m" "mpclass.m" "stringtest.m" "syntaxerr.m" "cellscript.m" "fontlock.m" "testeeval.m"))) :object-name "tests") matlab-mode/tests/mfuncnoendblock.m0000664000175000017500000000233014611713120020437 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . %{ 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 matlab-mode/tests/mpclass.m0000664000175000017500000000370614611713120016742 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . classdef (abstract) mpclass < handle & matlab.mixin.SetGetExactNames properties X Y end properties (Access='private') A B end methods function obj = mpclass(x, y) % Parsetest constructor obj.X = x; obj.Y = y; end end methods (Access='protected') function do_thing(obj, a, b) % Do a thing for the parse test obj.A = a; obj.B = b; localfunc('hello'); end end end function localfunc(T) % Local functions are handy. disp(T); end %% >> SEMANTIC TEST EXPECTED OUTPUT %{ (( "mpclass" type ( :type "class" :superclasses ("handle" "matlab.mixin.SetGetExactNames") :members ( ("X" variable) ("Y" variable) ("A" variable (:protection "private")) ("B" variable (:protection "private")) ("mpclass" function ( :return ("obj") :arguments ("x" "y"))) ("do_thing" function ( :protection "protected" :arguments ("obj" "a" "b")))))) ("localfunc" function ( :arguments ("T")))) %} %% End matlab-mode/tests/empty.m0000664000175000017500000000004614611713120016430 0ustar sebastiensebastien% Empty M file. % %%%empty guess guessmatlab-mode/tests/mfuncspacey.m0000664000175000017500000000164214611713120017612 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/blocks.m0000664000175000017500000001333114611713120016550 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . % >>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 matlab-mode/tests/complete.m0000664000175000017500000000305514611713120017105 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/dbtester.m0000664000175000017500000000237414611713120017114 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function OUT = dbtester() % An M file for exercising MATLAB's debugger APIs B = 1:.2:pi; OUT = localfunc_1(B); OUT = OUT + 1; end function OUT = localfunc_1(IN) % A local function if isempty(IN) IN = 1:.5:pi; end et = eltest.EmacsTest; OUT_TMP = et.dbtest(IN); OUT = localfunc_2(OUT_TMP(1:2:end)); end function A = localfunc_2(B) A = localfunc_3(B); end function A = localfunc_3(B) A = localfunc_4(B); end function A = localfunc_4(B) A = localfunc_5(B); end function A = localfunc_5(B) A = B; end matlab-mode/tests/mstest.el0000664000175000017500000005506314611713120016766 0ustar sebastiensebastien;;; mstest.el --- MATLAB Shell test suite ;; ;; Copyright (C) 2019-23 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; Test MATLAB shell by running MATLAB in an inferior process, and sending it ;; commands, and making sure we can read it's errors, and send it commands. (let* ((lf (or load-file-name (buffer-file-name (current-buffer)))) (d1 (file-name-directory lf)) (d (file-name-directory (directory-file-name d1))) ) (defvar mst-testfile-path d1 "Location of test MATLAB code.") (add-to-list 'load-path (expand-file-name d) t)) (defvar mst-testfile-path) ;; quiet compiler (require 'matlab-load) (require 'matlab) (require 'matlab-shell) (require 'comint) ;;; Code: (defun mstest-run-all-tests () "Run all the tests in this test file." ;; When debugging the test suite, make sure we can access the debugger. (unless noninteractive (toggle-debug-on-error) (toggle-debug-on-quit)) ;; Enable this to see how the input/output is interacting with the ;; test harness. (when (getenv "TESTDEBUG") (toggle-debug-on-error) (setq matlab-shell-io-testing t)) (mstest-start) (mstest-capture) (mstest-completion) (mstest-error-parse) (mstest-debugger) ) ;;; Startup Tests (defun mstest-start () "Test that we can start MATLAB, and that it runs the correct init file." (let ((process-connection-type nil)) ;; Use PIPE to prevent MATLAB from identifying the actual width of the terminal. ;; For some reason, batch mode emacs will choose a pty width of 10, which breaks up ;; text and makes it hard to test against. ;; A PIPE won't echo, so turn off that feature. (setq matlab-shell-echoes nil) (matlab-shell)) (let ((msb (matlab-shell-active-p))) (when (not msb) (error "MATLAB Shell command failed to create a shell buffer.")) (accept-process-output nil 1) (with-current-buffer msb (when (not (get-buffer-process msb)) (error "MATLAB Shell buffer failed to start process.")) ;; Check full startup. (let* ((start (current-time)) (elapsed nil)) (while (not matlab-prompt-seen) (setq elapsed (float-time (time-subtract nil start))) (when (> elapsed 20) (error "MATLAB Shell took to long (20s) to produce a prompt.")) (accept-process-output nil 1) (redisplay) (sit-for 1))) ;; During boot, we need to scrape the version number and release so we can load the ;; history file. Make sure that happend. (if matlab-shell-running-matlab-version (message "VERSION SCRAPE: Successfully found MATLAB Version %s" matlab-shell-running-matlab-version) (error "VERSION SCRAPE: Failed to find MATLAB Version number during startup.")) (message "PASS") ;; Make sure MATLAB thinks we have enough columns to display the rest of our tests. ;; Without the right number of columns, future tests will fail. (let ((txt (mstest-get-command-output "disp(get(0,'CommandWindowSize'))"))) (when (< (string-to-number txt) 80) (mstest-savestate) (error "COLUMNS TEST: Expecting a minimum of 80 columns, found %S" txt))) ;; Check that our path was added. (let ((txt (mstest-get-command-output "P=split(path,':');disp(P{1});")) (tbxdir (expand-file-name "toolbox" (file-name-directory (locate-library "matlab"))))) (message "PATH TEST: Expecting %S" tbxdir) (when (not (string= txt tbxdir)) (message "PATH TEST: Found %S" txt) (mstest-savestate) (error "MATLAB Shell failed to initialize with matlab-emacs as first entry on path.")) (message "PASS") ) ;; Make sure we have a ROOT (message "MATLABROOT TEST") (let ((txt (matlab-shell-matlabroot))) (message "Computed MATLAB ROOT as : %S" txt) (when (or (not txt) (string= txt "")) (mstest-savestate) (error "Failed to find MATLABROOT.")) ) (message "PASS") ;; Turn off beeps, because during tests they are annoying. (message "BEEP OFF TEST: ") (let ((txt (mstest-get-command-output "beep off; stat = beep; disp(stat)"))) (when (or (not txt) (not (string= txt "off"))) (mstest-savestate) (error "Expected BEEPS to be off, but found %S" txt))) (message "PASS") ;; Make sure that 'WHICH' works correctly. (message "WHICH TEST: ls") (let ((txt (car (matlab-shell-which-fcn "ls"))) (exp (expand-file-name "toolbox/matlab/general/ls.m" (matlab-shell-matlabroot)))) (if (string= txt exp) (message "PASS") (mstest-savestate) (error "Expected %s, but found %s" exp txt))) ))) ;;; Command Sending Tests (defun mstest-completion () "Test emacsdocomplete, and make sure it returns what we expect." (let ((msb (matlab-shell-active-p))) (when (not msb) (error "mstest-completion must run after mstest-start")) (with-current-buffer msb (goto-char (point-max)) ;; TEST completion fcn (message "COMPLETION TEST: emacs") (let* ((CLO (condition-case ERR (matlab-shell-completion-list "emacs") (error (mstest-savestate) (error "%S" ERR)))) (CL (cdr (nth 2 CLO))) (EXP '("emacs" "emacscd" "emacsdocomplete" "emacsinit" "emacsnetshell" "emacsrunregion")) (cnt 1)) (while (and CL EXP) (when (not (string= (car EXP) (car (car CL)))) (error "Expected %S /= %S TS for %d completion" (car EXP) (car (car CL)) cnt)) (setq cnt (1+ cnt) CL (cdr CL) EXP (cdr EXP)))) (message "PASS") ))) ;; Command Capture tests (defun mstest-capture () "Test the Emacs capturing output functionality." (save-window-excursion (let ((msb (matlab-shell-active-p))) (when (not msb) (error "mstest-completion must run after mstest-start")) ;; We'll be testing how windows split, etc. (switch-to-buffer msb) (delete-other-windows) (goto-char (point-max)) ;; TEST completion fcn (message "HELP TEST: ls") (let ((txt (mstest-get-command-output "help ls"))) (when (not (string= txt "\n")) (mstest-savestate) (message "Leftover text: [%s]" txt) (error "There should be no leftover text from help commands.")) (when (not (eq (current-buffer) msb)) (mstest-savestate) (error "Help command changed current buffer.")) (when (not (= (length (window-list)) 2)) (mstest-savestate) (error "Help command failed to create a 2nd window.")) (other-window 1) (when (not (string= (buffer-name) "*MATLAB Help: ls*")) (mstest-savestate) (error "Help command failed to create MATLAB Help buffer.")) (goto-char (point-min)) (when (not (looking-at "\\s-*LS\\s-+List")) (mstest-savestate) (error "Help ls command failed to populate help with LS help.")) (message "PASS")) (message "EVAL OUTPUT: testeeval") (let ((txt (mstest-get-command-output "testeeval"))) (when (not (string= txt "\n")) (mstest-savestate) (message "Leftover text: [%s]" txt) (error "There should be no leftover text from testeeval command.")) (when (or (not (stringp mstest-EVAL-TEST)) (not (string= mstest-EVAL-TEST "evaluate this"))) (mstest-savestate) (error "Emacs failed to evaluate command sent from testeeval MATLAB command.")) (message "PASS")) ))) ;;; Error Parsing (defun mstest-error-parse () "Test various errors, and if we can parse them." (let ((msb (matlab-shell-active-p))) (when (not msb) (error "mstest-error-parse must run after mstest-start")) (with-current-buffer msb (goto-char (point-max)) (mstest-error-command-check "buggy err" "buggy.m" 7) (mstest-error-command-check "buggy cmderr" "ls.m" -1) (mstest-error-command-check "buggy warn" "buggy.m" 15) (mstest-error-command-check "eltest.utils.testme" "testme.m" 7) (mstest-error-command-check "eltest.utils.testme(true)" "testme.m" 14) (mstest-error-command-check "et=eltest.EmacsTest; et.throwerr()" "EmacsTest.m" 17) ;; This must occur after assignment into variable et. (mstest-error-command-check "et.throwprop()" "EmacsTest.m" 22) (mstest-error-command-check "syntaxerr" "syntaxerr.m" 8) ))) (defun mstest-error-command-check (command file line) "Check that COMMAND produces an error that visits FILE at LINE. Assume we are in the MATLAB process buffer. If LINE is negative then do not test the line number." (message "ERRORS: %s" command) (let ((txt (mstest-get-command-output command))) (goto-char (point-max)) (save-window-excursion (condition-case ERR (matlab-shell-last-error) (error (mstest-savestate) (message "matlab-shell-last-error produced n error:") (error "%S" ERR)) (t (error "%S" ERR))) (let* ((bfn (buffer-file-name)) (bfnd (if bfn (file-name-nondirectory bfn) (buffer-name))) (ln (count-lines (point-min) (min (1+ (point)) (point-max)))) ) (when (not (string= bfnd file)) (mstest-savestate) (message "Err Text Generated:\n%S" txt) (error "Expected last error in %s. Found myself in %s" file (buffer-name))) (when (and (> line 0) (not (= ln line))) (mstest-savestate) (message "Err Text Generated:\n\n%S\n" txt) (error "Expected last error in %s on line %d. Found on line %d" file line ln)) )) (message "PASS") ;; Now CD someplace where these files are no longer on the path. (message "NO PATH ERRORS: %s" command) (mstest-get-command-output "cd('..')") ;; Re-do our last-error test to make sure it works when not on path. (save-window-excursion (condition-case ERR (matlab-shell-last-error) (error (mstest-savestate) (error "Error not found")) (t (error "%S" ERR))) (let* ((bfn (buffer-file-name)) (bfnd (if bfn (file-name-nondirectory bfn) (buffer-name))) (ln (count-lines (point-min) (min (1+ (point)) (point-max)))) ) (when (not (string= bfnd file)) (mstest-savestate) (error "Expected last error in %s. Found myself in %s" file (buffer-name))) (when (and (> line 0) (not (= ln line))) (mstest-savestate) (error "Expected last error in %s on line %d. Found on line %d" file line ln)) )) (mstest-get-command-output "cd('tests')") (message "PASS") )) (declare-function gud-break "gud") (declare-function gud-next "gud") (declare-function gud-cont "gud") ;;; Debugging: Breakpoints, stopping, visiting files (defun mstest-debugger () "Test debugging commands, and how MATLAB outputs state." (let ((msb (matlab-shell-active-p))) (when (not msb) (error "mstest-debugger must run after mstest-start")) (with-current-buffer msb (goto-char (point-max)) ;; Basic create/clear cycle. (mstest-debugger-breakpoint "dbstop in dbtester" "dbtester" "4") (mstest-debugger-breakpoint "dbclear all" nil nil)) (save-excursion (find-file (expand-file-name "dbtester.m" mst-testfile-path)) (goto-line 6) ;; Use gud fcn (mstest-debugger-breakpoint #'gud-break "dbtester" "6") (mstest-debugger-breakpointlist '(("dbtester" . 6))) (mstest-debugger-navto "dbtester" "dbtester.m" "6") (mstest-debugger-navto #'gud-next "dbtester.m" 8) (mstest-debugger-navto #'gud-next "dbtester.m" 10) (mstest-debugger-navto #'gud-cont "dbtester.m" -1) (goto-line 41) (mstest-debugger-breakpoint #'gud-break "dbtester" "6" "dbtester>localfunc_5" "41")) (mstest-debugger-breakpointlist '(("dbtester>localfunc_5" . 41) ("dbtester" . 6))) (find-file (expand-file-name "dbtester.m" mst-testfile-path)) (mstest-debugger-navto "dbtester" "dbtester.m" "6") (mstest-debugger-navto #'gud-cont "dbtester.m" 41) (mstest-debugger-navto #'gud-finish "dbtester.m" 37) (mstest-debugger-stacklist '(("localfunc_4" . 37) ("localfunc_3" . 33) ("localfunc_2" . 29) ("localfunc_1" . 24) ("dbtester" . 6) )) (mstest-debugger-navto #'gud-finish "dbtester.m" 33) (mstest-debugger-navto #'gud-cont "dbtester.m" -1) )) (defun mstest-debugger-stacklist (expectedstack) "Run ebstack, and check that a stack buffer appeared, and contains EXPECTEDSTACK" (message "DEBUG: Running ebstatus and checking for breakpoints buffer.") (let* ((txt (mstest-get-command-output "ebstack")) (buff (get-buffer "*MATLAB stack*")) (cnt 1) ) (with-current-buffer buff (goto-char (point-min)) (dolist (SK expectedstack) (unless (looking-at (format "\\s-*%d\\s-+\\(>>\\|--\\)\\s-+%s\\s-+%d" cnt (car SK) (cdr SK))) (mstest-savestate) (error "DEBUG: Stack buffer did not contain stack frame for %S, found [%s]" SK (buffer-substring (point-at-bol) (point-at-eol)))) (forward-line 1) (setq cnt (1+ cnt))) (message "PASS: Found %d matching breakpoints." (1- cnt)) ))) (defun mstest-debugger-breakpointlist (expectedbreakpoints) "Run ebstatus, and check that a breakpoit buffer appeared, and contains EXPECTEDBREAKPOINTS." (message "DEBUG: Running ebstatus and checking for breakpoints buffer.") (let* ((txt (mstest-get-command-output "ebstatus")) (buff (get-buffer "*MATLAB breakpoints*")) (cnt 1) ) (with-current-buffer buff (goto-char (point-min)) (dolist (BP expectedbreakpoints) (unless (looking-at (format "\\s-*%d\\s-+-\\s-+%s\\s-+%d" cnt (car BP) (cdr BP))) (mstest-savestate) (error "DEBUG: Breakpoints buffer did not contain breakpoint for %S, found [%s]" BP (buffer-substring (point-at-bol) (point-at-eol)))) (forward-line 1) (setq cnt (1+ cnt))) (message "PASS: Found %d matching breakpoints." (1- cnt)) ))) (defun mstest-debugger-navto (command fileexp lineexp &optional skipchecktxt) "Run some dbugger nav command, and verify we ended up in FILE at LINE. Command can be anything that will end with a K>> prompt, such as running the file that will hit a breakpoint, or dbstep, etc. COMMAND can be a string to run on the ML command prompt, or it can be a function." ;; In case test writer makes a mistake (when (stringp lineexp) (setq lineexp (string-to-number lineexp))) (message "DEBUG: Running %s until breakpoint" command) ;; Run the command, then call dbstatus to see what is there. (let ((txt (mstest-get-command-output command))) ;; TODO - test contents - should see a line with a # on it indicating the line ;; of text we stopped at. (let ((fname (file-name-nondirectory (buffer-file-name (current-buffer)))) (line (line-number-at-pos))) (if (<= lineexp 0) ;; Neg means we aren't debugging anymore. (when (matlab-on-debug-prompt-p) ;; on a debug prompt, this is a problem. (mstest-savestate) (error "DEBUG: Expected to have exited debug mode, but still on K>> prompt.")) ;; else, we should have jumped to some src code. (when (or (not (string= fname fileexp)) (and (not (= line lineexp)) (< lineexp 0))) (message "DEBUG: Expected %s line %d, ended up at %s line %d" fileexp lineexp fname line) (mstest-savestate) (error "DEBUG test failed")) ;; Text should start w/ a number, then some code (if (not skipchecktxt) (if (not (string-match "^\\s-*\\([0-9]+\\)\\>" txt)) (progn (mstest-savestate) (message "Command produced output : [%s]" txt) (error "DEBUG: Expected ML dugger to produce a line number. It did not.")) (let ((dbln (string-to-number (match-string 1 txt)))) (when (not (= dbln line)) (message "DEBUG: Expected %s line %d, ended up at %s %d" fileexp lineexp fname line) (mstest-savestate) (error "DEBUG test failed")))) (message "Skipping ML output line check.") ))) (message "PASS"))) (defun mstest-debugger-breakpoint (command &rest inputs) "Test setting a breakpoint withnnn COMMAND. COMMAND can be a string (command to send) or a function to run (such as a gud command). If a function, it will be called interactively. It should create a breakpoint in file FILEEXP on line LINEEXP. FILEEXP and LINEEXP can be lists. If a list, then there should be breakpoints set in the same order as specified." ;;(message "MSDB: %S -> %S %S" command fileexplst lineexplst) ;; Run the command, then call dbstatus to see what is there. (cond ((and (functionp command) (commandp command)) ;; We don't want to get command output. gud commands don't leave ;; anything behind. ;; (mstest-get-command-output command t) (call-interactively command) (accept-process-output nil 1) ;; don't care what output was. ) (t (mstest-get-command-output command)) ) ;; Get the breakpoint status to see what's there. (let ((txt (mstest-get-command-output "dbstatus"))) (while inputs (let ((fileexplst (car inputs)) (lineexplst (car (cdr inputs)))) (setq inputs (cdr (cdr inputs))) (when (stringp fileexplst) (setq fileexplst (list fileexplst))) (when (stringp lineexplst) (setq lineexplst (list lineexplst))) (if (not fileexplst) ;; this means no breakpoints. Check TXT is empty (progn (message "DEBUG: Expecting no breakpoints.") (when (not (string= txt "\n")) (message "DEBUG: Expected no breakpoints. Found '%S'." txt) (mstest-savestate) (error "DEBUG test failed")) (message "PASS")) ;; We are expecting breakpoints. Make sure they all match. (while (and fileexplst lineexplst) (let ((fileexp (car fileexplst)) (lineexp lineexplst)) (message "DEBUG: Expecting Breakpoint for %s line %S..." fileexp lineexp) (when (not (string-match "Breakpoint for \\([.>a-zA-Z0-9_]+\\) \\(is\\|are\\) on line " txt)) (message "DEBUG: No breakpoints found. dbstatus returned %S" txt) (mstest-savestate) (error "DEBUG test failed")) (let ((file (match-string-no-properties 1 txt)) (txtend (match-end 0))) ;; Check file name (when (not (string= file fileexp)) (message "DEBUG: Breakpoints in wrong place. Expected %S, found %S" fileexp file) (mstest-savestate) (error "DEBUG test failed")) ;; Loop over all the line numbers. (dolist (LN lineexp) (unless (string-match "[0-9]+" txt txtend) (message "DEBUG: Breakpoints text found. No line number found for expected %S" LN) (mstest-savestate) (error "DEBUG test failed")) (let ((line (match-string-no-properties 0 txt))) (setq txtend (match-end 0)) (when (not (string= line LN)) (message "DEBUG: Breakpoints in wrong place. Expected Line %S, found %S" LN line) (mstest-savestate) (error "DEBUG test failed")))) (message "PASS"))) (setq fileexplst (cdr fileexplst))))) ;; Trim the string, but only if there is more to do. (when inputs (unless (string-match "^\\s-*$" txt) (message "DEBUG: Expected multiple breakpoint sets, but found no separator before exptected sets: %S" inputs) (mstest-savestate) (error "DEBUG test failed")) (setq txt (substring txt (match-end 0))) ) ))) ;;; UTILITIES (defun mstest-cap-context (&optional start) "Return a string that represents context around point at this time." (when (not start) (setq start (point))) (concat (buffer-substring-no-properties (max (point-min) (- start 15)) start) "" (buffer-substring-no-properties start (min (point-max) (+ start 15))))) (defun mstest-get-command-output (command) "Wait for any pending output, and then return the text from lst command. Searches for the text between the last prompt, and the previous prompt." ;; Send the command. (let* ((ctxt nil) (start (with-current-buffer (matlab-shell-active-p) (goto-char (point-max)) (setq ctxt (mstest-cap-context)) (point))) ) ;; (when matlab-shell-io-testing ;; (message "Start CTXT: [%s]" ctxt)) (cond ;; For a string, send it ourselves ((stringp command) (matlab-shell-send-command command)) ;; For a command, call it interactively ((and (functionp command) (commandp command)) (call-interactively command)) ;; For a function, call it, expect it to send some command. ((functionp command) (funcall command)) ;; What is this? (t (error "Unkown command for mtest-get-command-output")) ) (with-current-buffer (matlab-shell-active-p) ;; Wait. (let ((starttime (current-time)) (totaltime nil) (matlab-shell-cco-testing t)) (when matlab-shell-io-testing (message "!!>")) (while (or (= (point) start) (not (matlab-on-empty-prompt-p))) (setq totaltime (float-time (time-subtract nil starttime))) (when (> totaltime 10) (let ((inhibit-field-text-motion t) (ctxte (mstest-cap-context))) (message "timeout: Start Ctxt: %S" ctxt) (message "timeout: End Ctxt: %S" ctxte) (mstest-savestate) (error "Timeout waiting for prompt. (%d elapsed seconds)" totaltime))) (redisplay) ;; Some filters also call accept-process-output inside this one ;; which causes it to not time out. (accept-process-output nil .5) (goto-char (point-max))))) (when matlab-shell-io-testing (message "!!<")) ;; Get the text from last prompt. (with-current-buffer (matlab-shell-active-p) (let ((inhibit-field-text-motion t) (endpt nil) (startpt nil)) ;; end pt is the end of the previous line from the last prompt. (goto-char (point-max)) (beginning-of-line) (forward-char -1) (setq endpt (point)) ;; start pt is at the line after the start pt. (goto-char start) (beginning-of-line) (if (looking-at "^K?>>\\s-*") (progn ;; The command was inserted. Skip it. (end-of-line) (forward-char 1)) ;; Any output text was deleted. Don't move the curosr ;; so we can grab the output. nil) (setq startpt (point)) (buffer-substring-no-properties startpt endpt) )))) (defun mstest-savestate () "Save output from *MATLAB* buffer when a test fails." (with-current-buffer (matlab-shell-active-p) (let* ((td (if (fboundp 'temporary-file-directory) (temporary-file-directory) temporary-file-directory)) (fn (expand-file-name "MATLABSHELL-BUFFER-CONTENST.txt" td))) (write-region (point-min) (point-max) fn) (message "Content of *MATLAB* buffer saved in %s" fn)))) (provide 'mstest) ;;; mstest.el ends here matlab-mode/tests/mfuncnofuncindent.m0000664000175000017500000000233514611713120021020 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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: %} matlab-mode/tests/mclass_cont.m0000664000175000017500000000161214611713120017577 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/mfuncends.m0000664000175000017500000000231414611713120017254 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/mfuncnoendindent.m0000664000175000017500000000156714611713120020641 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/testeeval.m0000664000175000017500000000257514611713120017277 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function testeeval(style) % Test that we can tell Emacs what to do. if nargin == 0 style = 'eval'; end switch style case 'eval' disp('(eval)') disp('(setq mstest-EVAL-TEST "evaluate this")') disp('') case 'buffer' disp('(*MATLAB TEST*)') pause(2) disp('Random text to display in a buffer') disp('Random text to display in a buffer') disp('Random text to display in a buffer') disp('Random text to display in a buffer') disp('') case 'noend' disp('(*MATLAB TEST*)') disp('Random text to display but can''t'); end end matlab-mode/tests/+eltest/0000775000175000017500000000000014611713120016467 5ustar sebastiensebastienmatlab-mode/tests/+eltest/@EmacsTest/0000775000175000017500000000000014611713120020457 5ustar sebastiensebastienmatlab-mode/tests/+eltest/@EmacsTest/EmacsTest.m0000664000175000017500000000237714611713120022536 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . classdef EmacsTest < handle % Class EMACSTEST % % Class that produces errors and file names we can test. properties prop1; end methods function h = EmacsTest() % Constructor end function throwerr(~) error('Error thrown from Emacs Test'); end function throwprop(et) et.prop1 = 1; et.prop2 = 2; % should error end function OUT = dbtest(h, IN) h.prop1 = IN; OUT = sin(h.prop1); end end end matlab-mode/tests/+eltest/+utils/0000775000175000017500000000000014611713120017702 5ustar sebastiensebastienmatlab-mode/tests/+eltest/+utils/testme.m0000664000175000017500000000167114611713120021366 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . function testme(belocal) % A Function that throws an error. if nargin == 1 && belocal localfcn(); else error('A problem in a package function.'); end end function localfcn() error('A problem in a package local fcn'); end matlab-mode/tests/continuations.m0000664000175000017500000001141514611713120020171 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . 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 matlab-mode/tests/errexamples.shell.m0000664000175000017500000000343514611713120020734 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . % This file contains sample text from a MATLAB shell buffer. % Use this for parsing tests for error trackers. % Newer MATLABs. This text copied from MATLAB R2019b % Errors: Error using ls (line 49) ls: cannot access 'blarg': No such file or directory Error in buggy (line 12) ls blarg % Errors: Error using buggy (line 7) You encounered an error in buggy.m % Syntax Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 8 Column: 12 Invalid expression. When calling a function or indexing a variable, use parentheses. Otherwise, check for mismatched delimiters. % Syntax Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 4 Column: 9 Character vector is not terminated properly. % Warning: Warning: You enountered a warning in buggy.m > In buggy (line 15) % Oldest MATLABs. This text taken from comments in MATLAB.el from % a long time ago. % Errors: >> ls Error in ==> buggy On line 12 ==> ls blarg % Errors: Error using ==> buggy at 7 % Syntax: Syntax error in ==> syntaxerr.m On line 8 ==> B = A(1 % Warning: In buggy at line 15 You encountered a warning % End matlab-mode/tests/cellscript.m0000664000175000017500000000150414611713120017436 0ustar sebastiensebastien% Copyright (C) 2023 Eric Ludlam (and others) % 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 . %% Script with Cells in it for testing 'run cell' % if true A = 1:10 end %% Cell 2 if true B = 10:15 end %% cell 3 if true C = -10:-1 end matlab-mode/matlab-shell.el0000664000175000017500000027411214625574731016672 0ustar sebastiensebastien;;; matlab-shell.el --- Run MATLAB in an inferior process ;; ;; Copyright (C) 2019 Eric Ludlam ;; ;; Author: Eric Ludlam ;; ;; 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: ;; ;; This library supports a MATLAB shell buffer, which runs MATLAB in ;; an inferior shell. Supports working with the MATLAB command line, ;; and the MATLAB debugger. ;; ;;; Code: (require 'matlab) (require 'matlab-compat) (require 'comint) (require 'server) (eval-and-compile (require 'mlgud) (require 'shell) ) ;; Silence warnings from company.el (declare-function company-mode "company") (defvar company-idle-delay) (defvar company-mode) ;; Key entry points for matlab-shell-gud (declare-function matlab-shell-mode-gud-enable-bindings "matlab-shell-gud") (declare-function matlab-shell-gud-startup "matlab-shell-gud") ;;; Customizations ;; ;; Options to configure using matlab-shell (defgroup matlab-shell nil "MATLAB shell mode." :prefix "matlab-shell-" :group 'matlab) ;; ;; Shell Startup (defcustom matlab-shell-mode-hook nil "*List of functions to call on entry to MATLAB shell mode." :group 'matlab-shell :type 'hook) (defcustom matlab-shell-command "matlab" "*The name of the command to be run which will start the MATLAB process." :group 'matlab-shell :type 'string) (defcustom matlab-shell-command-switches '("-nodesktop") "*Command line parameters run with `matlab-shell-command'. Command switches are a list of strings. Each entry is one switch." :group 'matlab-shell :type '(list :tag "Switch: ")) (defface matlab-shell-error-face (list (list t (list :background 'unspecified :foreground "red1" :bold t))) "*Face to use when errors occur in MATLAB shell." :group 'matlab-shell) (defcustom matlab-custom-startup-command nil "Custom MATLAB command to be run at startup." :group 'matlab-shell :type 'string) (defcustom matlab-shell-echoes t "*If `matlab-shell-command' echoes input." :group 'matlab-shell :type 'boolean) (defcustom matlab-shell-history-file "~/.matlab/%s/history.m" "*Location of the history file. A %s is replaced with the MATLAB version release number, such as R12. This file is read to initialize the comint input ring." :group 'matlab-shell :type 'filename) (defcustom matlab-shell-history-ignore "^%\\|%%$\\|emacs.set" "Regular expression matching items from history to ignore. This expression should ignore comments (between sessions) and any command that ends in 2 or more %%, added to automatic commands." :group 'matlab-shell :type 'filename) (defcustom matlab-shell-autostart-netshell nil "Use the netshell side-channel for communicating with MATLAB." :group 'matlab-shell :type 'boolean) ;; ;; Edit from MATLAB (defcustom matlab-shell-emacsclient-command (matlab-find-emacsclient) "*The command to use as an external editor for MATLAB. Using emacsclient allows the currently running Emacs to also be the external editor for MATLAB. Setting this to the empty string will disable use emacsclient as the external editor." :group 'matlab-shell :type 'integer) ;; ;; Run from Emacs (defcustom matlab-shell-run-region-function 'auto "Technique to use for running a line, region, or cell. There are different benefits to different kinds of commands. Use 'auto to guess which to use by looking at the environment. auto - guess which to use matlab-shell-region->commandline - Extract region, and generate 1 line of ML code. matlab-shell-region->script - Extract region and any local fcns, and write to tmp script. Call that from MATLAB. matlab-shell-region->internal - Send region location to MATLAB, and have ML extract and run that region. Customize `matlab-shell-emacsrunregion' to specify what ML function to use for this." :group 'matlab-shell :type '(choice (const :tag "Auto" auto) (const :tag "Extract Line" matlab-shell-region->commandline) (const :tag "Extract Script" matlab-shell-region->script) (const :tag "Matlab Extract" matlab-shell-region->internal))) (defcustom matlab-shell-internal-emacsrunregion "emacsrunregion" "The MATLAB command to use for running a region. This command is used when `matlab-shell-run-region-function' is set to auto, or `matlab-shell-region->internal'" :group 'matlab-shell :type 'string) ;; ;; Features in an active shell (defcustom matlab-shell-input-ring-size 32 "*Number of history elements to keep." :group 'matlab-shell :type 'integer) ;; ;; Completion handling (defcustom matlab-shell-ask-MATLAB-for-completions t "When Non-nil, ask MATLAB for a completion list. When nil, complete against file names." :group 'matlab-shell :type 'boolean) (defcustom matlab-shell-tab-use-company t "*Use `company' (complete anything) for TAB completions in `matlab-shell'. Only effective when when `company' is installed. Note, when you type to narrow completions, you may find the responses slow and if so, you can try turning this off." :group 'matlab-shell :type 'boolean) (defvar matlab-shell-tab-company-available (if (locate-library "company") t nil) "Non-nil if we have `company' installed. Use this to override initial check.") (defvar matlab-shell-errorscanning-syntax-table (let ((st (copy-syntax-table matlab-mode-syntax-table))) ;; Make \n be whitespace when scanning output. (modify-syntax-entry ?\n " " st) st) "Syntax table used when scanning MATLAB output. In this case, comment and \n are not special, as word wrap can get in the way.") (defvar matlab-shell-prompt-appears-hook nil "Hooks run each time a prompt is seen and sent to display. If multiple prompts are seen together, only call this once.") (defvar matlab-shell-prompt-hook-cookie nil "Cookie used to transfer info about detected prompts from inner filter to outer.") (make-variable-buffer-local 'matlab-shell-prompt-hook-cookie) (defvar matlab-shell-suppress-prompt-hooks nil "Non-nil to suppress running prompt hooks.") (defvar matlab-shell-cco-testing nil "Non nil when testing `matlab-shell'.") (defvar matlab-shell-io-testing nil "Non-nil to display process output and input log.") ;;; Font Lock ;; ;; Extra font lock keywords for the MATLAB shell. (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 '(" M A T L A B " 0 'underline) '("All Rights Reserved" 0 'italic) '("\\(\\(?:(c)\\)?\\s-+Copyright[^\n]+\\)" 1 font-lock-comment-face) '("\\(Version\\)\\s-+\\([^\n]+\\)" (1 font-lock-function-name-face) (2 font-lock-variable-name-face)) '("\\(R[0-9]+[ab]\\(?: Update [0-9]+\\)\\) \\([^\n]+\\)" (1 font-lock-function-name-face) (2 font-lock-variable-name-face)) '("^To get started, type doc.$" 0 font-lock-comment-face prepend) '("For product information, [^\n]+" 0 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)) ) "Highlight various extra outputs that are typical for MATLAB.") (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.") (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 for MATLAB shell.") ;;; ROOT ;; ;;;###autoload (defun matlab-mode-determine-matlabroot () "Return the MATLABROOT for the 'matlab-shell-command'." (let ((path (file-name-directory matlab-shell-command))) ;; if we don't have a path, find the MATLAB executable on our path. (when (not path) (setq path (matlab-find-executable-directory matlab-shell-command))) (when path ;; When we find the path, we need to massage it to identify where ;; the M files are that we need for our completion lists. (if (string-match "/bin/?$" path) (setq path (substring path 0 (match-beginning 0))))) path)) ;;; Keymaps & Menus ;; (defvar matlab-shell-mode-map (let ((km (make-sparse-keymap 'matlab-shell-mode-map))) ;; Mostly use comint mode's map. (matlab-set-keymap-parent km comint-mode-map) ;; We can jump to errors, so take over this keybinding. (substitute-key-definition 'next-error 'matlab-shell-last-error km global-map) ;; Interrupt (define-key km [(control c) (control c)] 'matlab-shell-interrupt-subjob) ;; Help system (define-key km [(control h) (control m)] matlab-help-map) ;; Completion (define-key km (kbd "TAB") 'matlab-shell-tab) (define-key km "\C-i" 'matlab-shell-tab) (define-key km (kbd "") 'matlab-shell-c-tab) ;; Command history (define-key km [(control up)] 'comint-previous-matching-input-from-input) (define-key km [(control down)] 'comint-next-matching-input-from-input) (define-key km [up] 'matlab-shell-previous-matching-input-from-input) (define-key km [down] 'matlab-shell-next-matching-input-from-input) ;; Editing (define-key km [(control return)] 'comint-kill-input) (define-key km [(backspace)] 'matlab-shell-delete-backwards-no-prompt) ;; Files (define-key km "\C-c." 'matlab-shell-locate-fcn) ;; matlab-shell actions (define-key km "\C-c/" 'matlab-shell-sync-buffer-directory) km) "Keymap used in `matlab-shell-mode'.") (easy-menu-define matlab-shell-menu matlab-shell-mode-map "MATLAB shell menu" '("MATLAB" ["Goto last error" matlab-shell-last-error t] "----" ["Stop On Errors" matlab-shell-dbstop-error t] ["Don't Stop On Errors" matlab-shell-dbclear-error t] "----" ["Locate MATLAB function" matlab-shell-locate-fcn :help "Run 'which FCN' in matlab-shell, then open the file in Emacs"] ["Run Command" matlab-shell-run-command t] ["Describe Variable" matlab-shell-describe-variable t] ["Describe Command" matlab-shell-describe-command t] ["Lookfor Command" matlab-shell-apropos t] "----" ["Complete command" matlab-shell-tab t] "----" ["Demos" matlab-shell-demos t] ["Close Current Figure" matlab-shell-close-current-figure t] ["Close Figures" matlab-shell-close-figures t] "----" ["Sync buffer directory (emacscd)" matlab-shell-sync-buffer-directory :help "Sync the matlab-shell buffer `default-directory' with MATLAB's pwd.\n\ These will differ when MATLAB code changes directory without notifying Emacs."] ["Customize" (customize-group 'matlab-shell) (and (featurep 'custom) (fboundp 'custom-declare-variable)) ] ["Exit" matlab-shell-exit t])) (easy-menu-add matlab-shell-menu matlab-shell-mode-map) ;;; MODE ;; ;; The Emacs major mode for interacting with the matlab shell process. (defvar matlab-shell-last-error-anchor) ;; Quiet compiler warning (defun matlab-shell-mode () "Run MATLAB as a subprocess in an Emacs buffer. This mode will allow standard Emacs shell commands/completion to occur with MATLAB running as an inferior process. Additionally, this shell mode is integrated with `matlab-mode', a major mode for editing M code. > From an M file buffer: \\ \\[matlab-shell-save-and-go] - Save the current M file, and run it in a \ MATLAB shell. > From Shell mode: \\ \\[matlab-shell-last-error] - find location of last MATLAB runtime error \ in the offending M file. > From an M file, or from Shell mode: \\ \\[matlab-shell-run-command] - Run COMMAND and show result in a popup buffer. \\[matlab-shell-describe-variable] - Show variable contents in a popup buffer. \\[matlab-shell-describe-command] - Show online documentation for a command \ in a popup buffer. \\[matlab-shell-apropos] - Show output from LOOKFOR command in a popup buffer. > Keymap: \\{matlab-mode-map}" (setq major-mode 'matlab-shell-mode mode-name "M-Shell" comint-prompt-regexp "^\\(K\\|EDU\\)?>> *" comint-delimiter-argument-list (list [ 59 ]) ; semi colon comint-dynamic-complete-functions '(comint-replace-by-expanded-history) comint-process-echoes matlab-shell-echoes comint-get-old-input #'matlab-comint-get-old-input ) ;; Shell Setup (require 'shell) ;; COMINT History Setup (set (make-local-variable 'comint-input-ring-size) matlab-shell-input-ring-size) (set (make-local-variable 'comint-input-ring-file-name) (format matlab-shell-history-file "R12")) (if (fboundp 'comint-read-input-ring) (comint-read-input-ring t)) ;;; MODE Settings (make-local-variable 'comment-start) (setq comment-start "%") (use-local-map matlab-shell-mode-map) (set-syntax-table matlab-mode-syntax-table) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((matlab-shell-font-lock-keywords-1 matlab-shell-font-lock-keywords-2 matlab-shell-font-lock-keywords-3) t nil ((?_ . "w")))) ;; GUD support (matlab-shell-mode-gud-enable-bindings) ;; Company mode can be used to display completions for MATLAB in matlab-shell. ;; This block enables company mode for this shell, and turns off the idle timer ;; so users must press TAB to get the menu. (when (and matlab-shell-tab-use-company matlab-shell-tab-company-available) ;; Only do popup when users presses TAB (set (make-local-variable 'company-idle-delay) nil) (company-mode)) ;; Hooks, etc (run-hooks 'matlab-shell-mode-hook) (matlab-show-version) ) ;;; NETSHELL integration ;; (declare-function matlab-netshell-client "matlab-netshell") (declare-function matlab-netshell-server-start "matlab-netshell") (declare-function matlab-netshell-server-active-p "matlab-netshell") (declare-function matlab-netshell-eval "matlab-netshell") (defun matlab-netshell-active-p () "Return t if the MATLAB netshell is active." (when (featurep 'matlab-netshell) (matlab-netshell-client))) (defun matlab-any-shell-active-p () "Return non-nil of any of the matlab connections are active." (or (matlab-netshell-active-p) (matlab-shell-active-p))) ;;; MATLAB SHELL ;; ;; Core shell state handling & startup function. (defvar matlab-shell-buffer-name "MATLAB" "Name used to create `matlab-shell' mode buffers. This name will have *'s surrounding it.") (defvar matlab-prompt-seen nil "Track visibility of MATLAB prompt in MATLAB Shell.") (defun matlab-shell-active-p () "Return t if the MATLAB shell is active." (let ((msbn (get-buffer (concat "*" matlab-shell-buffer-name "*")))) (if msbn (with-current-buffer msbn (if (comint-check-proc (current-buffer)) (current-buffer)))))) ;;;###autoload (defun matlab-shell () "Create a buffer with MATLAB running as a subprocess. MATLAB shell cannot work on the MS Windows platform because MATLAB is not a console application." (interactive) ;; MATLAB shell does not work by default on the Windows platform. Only ;; permit it's operation when the shell command string is different from ;; the default value. (True when the engine program is running.) (when (and (or (eq window-system 'pc) (eq window-system 'w32)) (string= matlab-shell-command "matlab")) (error "MATLAB cannot be run as a inferior process. \ Try C-h f matlab-shell RET")) (require 'shell) (require 'matlab-shell-gud) ;; Make sure netshell is started if it is wanted. (when (and matlab-shell-autostart-netshell (not (matlab-netshell-server-active-p))) (matlab-netshell-server-start)) ;; Show the shell buffer (switch-to-buffer (concat "*" matlab-shell-buffer-name "*")) ;; If the shell isn't active yet, start it. (when (not (matlab-shell-active-p)) ;; Clean up crufty state (kill-all-local-variables) ;; Thx David Chappaz for reminding me about this patch. (let* ((windowid (frame-parameter (selected-frame) 'outer-window-id)) (newvar (concat "WINDOWID=" windowid)) (process-environment (cons newvar process-environment))) (apply #'make-comint matlab-shell-buffer-name matlab-shell-command nil matlab-shell-command-switches)) ;; Enable GUD (matlab-shell-gud-startup) ;; Init our filter and sentinel (set-process-filter (get-buffer-process (current-buffer)) 'matlab-shell-wrapper-filter) (set-process-sentinel (get-buffer-process (current-buffer)) 'matlab-shell-wrapper-sentinel) ;; XEmacs has problems w/ this variable. Set it here. (set-marker comint-last-output-start (point-max)) (make-local-variable 'matlab-prompt-seen) (setq matlab-prompt-seen nil) ;; FILTERS ;; ;; Add hook for finding the very first prompt - so we know when the buffer is ready to use. (add-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-first-prompt-fcn) ;; Track current directories when user types cd (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t) ;; patch Eli Merriam ;; Add a version scraping logo identification filter. (add-hook 'comint-output-filter-functions 'matlab-shell-version-scrape nil t) ;; Add pseudo html-renderer (add-hook 'comint-output-filter-functions 'matlab-shell-render-html-anchor nil t) ;; Scroll to bottom after running cell/region (add-hook 'comint-output-filter-functions 'comint-postoutput-scroll-to-bottom nil t) ;; Add error renderer to prompt hook so the prompt is available for resolving names. (make-local-variable 'matlab-shell-last-error-anchor) (setq matlab-shell-last-error-anchor nil) (add-hook 'matlab-shell-prompt-appears-hook 'matlab-shell-render-errors-as-anchor nil t) (add-hook 'matlab-shell-prompt-appears-hook 'matlab-shell-colorize-errors nil t) ;; Comint and GUD both try to set the mode. Now reset it to ;; matlab mode. (matlab-shell-mode)) ) ;;; PROCESS FILTERS & SENTINEL ;; ;; These are wrappers around the GUD filters so we can pre and post process ;; decisions by comint and mlgud. (defvar matlab-shell-capturetext-start-text "" "Text used as simple signal for text that should be captured.") (defvar matlab-shell-capturetext-end-text "" "Text used as simple signal for text that should be captured.") (defvar matlab-shell-accumulator "" "Accumulate text that is being captured.") (make-variable-buffer-local 'matlab-shell-accumulator) (defvar matlab-shell-flush-accumulation-buffer nil "When non-nil, flush the accumulation buffer.") (defvar matlab-shell-in-process-filter nil "Non-nil when inside `matlab-shell-wrapper-filter'.") (defun matlab-shell-wrapper-filter (proc string) "MATLAB Shell's process filter. This wraps the GUD and COMINT filters. PROC is the process with input to this filter. STRING is the recent output from PROC to be filtered." ;; A few words about process sentinel's in the MATLAB shell buffer: ;; Our filter calls the GUD filter. ;; The GUD filter calls the COMINT filter. ;; The COMINT filter writes output to the buffer and runs filters. ;; We need to run our error anchor commands AFTER all of the above is done, ;; but ONLY when we have an empty prompt and can ask MATLAB more questions. ;; We need this filter to provide a hook on prompt display when everything ;; has been processed. (let ((buff (process-buffer proc)) (captext nil) (matlab-shell-in-process-filter t)) ;; Cleanup garbage before sending it along to the other filters. (let ((garbage (concat "\\(" (regexp-quote "\C-g") "\\|" (regexp-quote "\033[H0") "\\|" (regexp-quote "\033[H\033[2J") "\\|" (regexp-quote "\033H\033[2J") "\\)"))) (while (string-match garbage string) ;;(if (= (aref string (match-beginning 0)) ?\C-g) ;;(beep t)) (setq string (replace-match "" t t string)))) ;; Engage the accumulator (setq matlab-shell-accumulator (concat matlab-shell-accumulator string) string "") ;; STARTCAP - push preceeding text to output. (if (and (not matlab-shell-flush-accumulation-buffer) (string-match (regexp-quote matlab-shell-capturetext-start-text) matlab-shell-accumulator)) (progn (setq string (substring matlab-shell-accumulator 0 (match-beginning 0)) matlab-shell-accumulator (substring matlab-shell-accumulator (match-beginning 0))) ;; START and ENDCAP - save captured text, and push trailing text to output (when (string-match (concat (regexp-quote matlab-shell-capturetext-end-text) "\\(:?\n\\)?") matlab-shell-accumulator) ;; If no end, then send anything before the CAP, and accumulate everything ;; else. (setq string (concat string (substring matlab-shell-accumulator (match-end 0))) captext (substring matlab-shell-accumulator 0 (match-end 0)) matlab-shell-accumulator ""))) ;; No start capture, or an ended capture, everything goes back to String (setq string (concat string matlab-shell-accumulator) matlab-shell-accumulator "" matlab-shell-flush-accumulation-buffer nil)) (with-current-buffer buff (mlgud-filter proc string)) ;; In case things get switched around on us (with-current-buffer buff (when matlab-shell-prompt-hook-cookie (setq matlab-shell-prompt-hook-cookie nil) (run-hooks 'matlab-shell-prompt-appears-hook)) ) ;; If there was some captext, process it, but only after doing all the other important ;; stuff. (when captext (matlab-shell-process-capture-text captext)) )) (defun matlab-shell-wrapper-sentinel (proc string) "MATLAB Shell's process sentinel. This wraps the GUD and COMINT filters. PROC is the function which experienced a change in state. STRING is a description of what happened." (let ((buff (process-buffer proc))) (with-current-buffer buff (mlgud-sentinel proc string)))) ;;; COMINT support fcns ;; (defun matlab-comint-get-old-input () "Compute text from the current line to evaluate with MATLAB. This function checks to make sure the line is on a prompt. If not, it returns empty string" (let ((inhibit-field-text-motion t)) (save-excursion (beginning-of-line) (save-match-data (if (looking-at comint-prompt-regexp) ;; We'll send this line. (buffer-substring-no-properties (match-end 0) (point-at-eol)) ;; Otherwise, it's probably junk that is useless. Don't do it. ""))))) ;;; STARTUP / VERSION ;; ;; Handlers for startup output / version scraping ;; ;; TODO - these scraped values aren't used anywhere. Do we care? (defvar matlab-shell-running-matlab-version nil "The version of MATLAB running in the current `matlab-shell' buffer.") (defvar matlab-shell-running-matlab-release nil "The release of MATLAB running in the current `matlab-shell' buffer.") (defun matlab-shell-version-scrape (str) "Scrape the MATLAB Version from the MATLAB startup text. Argument STR is the string to examine for version information." (if (string-match "\\(Version\\)\\s-+\\([.0-9]+\\)\\s-+(\\(R[.0-9]+[ab]?\\))" str) ;; OLDER MATLAB'S (setq matlab-shell-running-matlab-version (match-string 2 str) matlab-shell-running-matlab-release (match-string 3 str)) ;; NEWER MATLAB'S (if (string-match "\\(R[0-9]+[ab]\\)\\s-+\\(?:Update\\s-+[0-9]+\\s-+\\|Prerelease\\s-+\\)?(\\([0-9]+\\.[0-9]+\\)\\." str) (setq matlab-shell-running-matlab-version (match-string 2 str) matlab-shell-running-matlab-release (match-string 1 str)))) ;; Notice that this worked. (when matlab-shell-running-matlab-version ;; Remove the scrape from our list of things to do. We are done getting the version. (remove-hook 'comint-output-filter-functions 'matlab-shell-version-scrape t) (message "Detected MATLAB %s (%s) -- Loading history file" matlab-shell-running-matlab-release matlab-shell-running-matlab-version) ;; Now get our history loaded (setq comint-input-ring-file-name (format matlab-shell-history-file matlab-shell-running-matlab-release) comint-input-history-ignore matlab-shell-history-ignore) (if (fboundp 'comint-read-input-ring) (comint-read-input-ring t)) )) ;;; ANCHORS ;; ;; Scan output for text, and turn into navigable links. (defvar gud-matlab-marker-regexp-prefix "error:\\|opentoline\\|dbhot" "A prefix to scan for to know if output might be scarfed later.") (defvar matlab-shell-html-map (let ((km (make-sparse-keymap))) (if (string-match "XEmacs" emacs-version) (define-key km [button2] 'matlab-shell-html-click) (define-key km [mouse-2] 'matlab-shell-html-click) (define-key km [mouse-1] 'matlab-shell-html-click)) (define-key km [return] 'matlab-shell-html-go) km) "Keymap used on overlays that represent errors.") ;; Anchor expressions. (defvar matlab-anchor-beg "" "Beginning of html anchor.") (defvar matlab-anchor-end "" "End of html anchor.") (defun matlab-shell-render-html-anchor (str) "Render html anchors inserted into the MATLAB shell buffer. Argument STR is the text for the anchor." (when (string-match matlab-anchor-end str) (save-excursion (with-syntax-table matlab-shell-errorscanning-syntax-table (while (re-search-backward matlab-anchor-beg ;; Arbitrary back-buffer. We don't ;; usually get text in such huge chunks (max (point-min) (- (point-max) 8192)) t) (let* ((anchor-beg-start (match-beginning 0)) (anchor-beg-finish (match-end 0)) (anchor-text (match-string 1)) (anchor-end-finish (search-forward matlab-anchor-end)) (anchor-end-start (match-beginning 0)) (o (matlab-make-overlay anchor-beg-finish anchor-end-start))) (matlab-overlay-put o 'mouse-face 'highlight) (matlab-overlay-put o 'face 'underline) (matlab-overlay-put o 'matlab-url anchor-text) (matlab-overlay-put o 'keymap matlab-shell-html-map) (matlab-overlay-put o 'help-echo anchor-text) (delete-region anchor-end-start anchor-end-finish) (delete-region anchor-beg-start anchor-beg-finish) )))))) ;;; ERROR HANDLING ;; ;; The regular expression covers to forms in tests/erroexamples.shell.m ;; (defvar matlab-shell-error-anchor-expression (concat "^>?\\s-*\\(\\(Error \\(in\\|using\\)\\s-+\\|Syntax error in \\)\\(?:==> \\)?\\|" "In\\s-+\\(?:workspace belonging to\\s-+\\)?\\|Error:\\s-+File:\\s-+\\|Warning:\\s-+[^\n]+\n\\)") "Expressions used to find errors in MATLAB process output. This variable contains the anchor, or starting text before a typical error. See `matlab-shell-error-location-expression' for a list of expressions for identifying where the error is after this anchor.") (defvar matlab-shell-error-location-expression (list ;; Pulled from R2019b "\\(?:^> In\\s-+\\)?\\([-+>@.a-zA-Z_0-9/ \\\\:]+\\)\\s-+(line \\([0-9]+\\))" "\\([-+>@.a-zA-Z_0-9/ \\\\:]+\\)\\s-+Line:\\s-+\\([0-9]+\\)\\s-+Column:\\s-+\\([0-9]+\\)" ;; Oldest I have examples for: (concat "\\([-+>@.a-zA-Z_0-9/ \\\\:]+\\)\\(?:>[^ ]+\\)?.*[\n ]" "\\(?:On\\|at\\)\\(?: line\\)? \\([0-9]+\\) ?") ) "List of Expressions to search for after an error anchor is found. These expressions are listed as matching from newer MATLAB versions to older MATLAB's. Each expression should have the following match strings: 1 - The matlab function 2 - The line number 3 - The column number (if available)") ;; (global-set-key [f7] 'matlab-shell-scan-for-error-test) (defun matlab-shell-scan-for-error-test () "Interactively try out the error scanning feature." (interactive) (let ((ans (matlab-shell-scan-for-error (point-min)))) (when ans (pulse-momentary-highlight-region (car ans) (car (cdr ans)))) (message "Found: %S" ans))) (defun matlab-shell-scan-for-error (limit) "Scan backward for a MATLAB error in the current buffer until LIMIT. Uses `matlab-shell-error-anchor-expression' to find the error. Uses `matlab-shell-error-location-expression' to find where the error is. Returns a list of the form: ( STARTPT ENDPT FILE LINE COLUMN )" (with-syntax-table matlab-shell-errorscanning-syntax-table (let ((ans nil) (beginning nil)) (when (re-search-backward matlab-shell-error-anchor-expression limit t) (save-excursion (setq beginning (save-excursion (goto-char (match-beginning 0)) (back-to-indentation) (point))) (goto-char (match-end 0)) (dolist (EXP matlab-shell-error-location-expression) (when (looking-at EXP) (setq ans (list beginning (match-end 0) (match-string-no-properties 1) (match-string-no-properties 2) (match-string-no-properties 3) ))))) ) ans))) (defvar matlab-shell-last-error-anchor nil "Last point where an error anchor was set.") (defvar matlab-shell-last-anchor-as-frame nil ;; NOTE: this isn't being used yet. "The last error anchor saved, represented as a debugger frame.") (defun matlab-shell-render-errors-as-anchor (&optional str) "Hook function run when process filter sees a prompt. Detect non-url errors, and treat them as if they were url anchors. Input STR is provided by comint but is unused." (save-excursion ;; Move to end to make sure we are scanning the new stuff. (goto-char (point-max)) ;; We have found an error stack to investigate. (let ((first nil) (ans nil) (overlaystack nil) (starting-anchor matlab-shell-last-error-anchor) (newest-anchor matlab-shell-last-error-anchor) ) (while (setq ans (matlab-shell-scan-for-error (or starting-anchor (point-min)))) (let* ((err-start (nth 0 ans)) (err-end (nth 1 ans)) (err-file (matlab-string-trim (nth 2 ans))) (err-line (nth 3 ans)) (err-col (nth 4 ans)) (o (matlab-make-overlay err-start err-end)) (err-mref-deref (matlab-shell-mref-to-filename err-file)) (err-full-file (when err-mref-deref (expand-file-name err-mref-deref))) (url (concat "opentoline('" (or err-full-file err-file) "'," err-line ",0)")) ) ;; Setup the overlay with the URL. (matlab-overlay-put o 'mouse-face 'highlight) (matlab-overlay-put o 'face 'underline) ;; The url will recycle opentoline code. (matlab-overlay-put o 'matlab-url url) (matlab-overlay-put o 'matlab-fullfile err-full-file) (matlab-overlay-put o 'keymap matlab-shell-html-map) (matlab-overlay-put o 'help-echo (concat "Jump to error at " (or err-full-file err-file) ".")) (setq first url) (push o overlaystack) ;; Save as a frame (setq matlab-shell-last-anchor-as-frame (cons err-file err-line)) (setq newest-anchor (max (or newest-anchor (point-min)) err-end)) )) ;; Keep track of the very first error in this error stack. ;; It will represent the "place to go" for "go-to-last-error". (dolist (O overlaystack) (matlab-overlay-put O 'first-in-error-stack first)) ;; Once we've found something, don't scan it again. (when overlaystack (setq matlab-shell-last-error-anchor (save-excursion (goto-char newest-anchor) (point-marker))))))) (defvar matlab-shell-errortext-start-text "\n" "Text used as a signal for errors.") (defvar matlab-shell-errortext-end-text "" "Text used as a signal for errors.") (defun matlab-shell-colorize-errors (&optional str) "Hook function run to colorize MATLAB errors. The filter replaces indicators with text . This strips out that text, and colorizes the region red. STR is provided by COMINT but is unused." (save-excursion (let ((start nil) (end nil) ) (goto-char (point-max)) (while (re-search-backward (regexp-quote matlab-shell-errortext-end-text) nil t) ;; Start w/ end text to make sure everything is in the buffer already. ;; Then scan for the beginning, and start there. As we delete text, locations will move, ;; so move downward after this. (if (not (re-search-backward (regexp-quote matlab-shell-errortext-start-text) nil t)) (error "Mismatched error text tokens from MATLAB") ;; Save off where we start, and delete the indicator. (setq start (match-beginning 0)) (delete-region start (match-end 0)) ;; Find the end. (if (not (re-search-forward (regexp-quote matlab-shell-errortext-end-text) nil t)) (error "Internal error scanning for error text tokens") (setq end (match-beginning 0)) (delete-region end (match-end 0)) ;; Now colorize the text. Use overlay because font-lock messes with font properties. (let ((o (matlab-make-overlay start end (current-buffer) nil nil)) ) (matlab-overlay-put o 'shellerror t) (matlab-overlay-put o 'face 'matlab-shell-error-face) ))) ;; Setup for next loop (goto-char (point-max)))))) ;;; Shell Startup (defun matlab-shell--get-emacsclient-command () "Compute how to call emacsclient so MATLAB will connect to this Emacs. Handles case of multiple Emacsen from different users running on the same system." (when (not (server-running-p)) ;; We need an Emacs server for ">> edit foo.m" which leverages to ;; emacsclient to open the file in the current Emacs session. Be ;; safe and start a server with a unique name. This ensures that ;; we don't have multiple emacs sessions stealing the server from ;; each other. (setq server-name (format "server-%d" (emacs-pid))) (message "matlab-shell: starting server with name %s" server-name) (server-start) (when (not (server-running-p)) (user-error "Unable to start server with name %s" server-name))) (let ((iq (if (eq system-type 'windows-nt) ;; Probably on Windows, probably in "Program Files" - ;; we need to quote this thing. ;; SADLY - emacs Edit command also wraps the command in ;; quotes - but we have to include arguments - so we need ;; to add internal quotes so the quotes land in the right place ;; when MATLAB adds external quotes. "\"" ""))) (concat matlab-shell-emacsclient-command iq " -n" (if server-use-tcp (concat " -f " iq (expand-file-name server-name server-auth-dir)) (concat " -s " iq (expand-file-name server-name server-socket-dir)))))) (defvar matlab-shell-use-emacs-toolbox ;; matlab may not be on path. (Name change, explicit load, etc) (let* ((mlfile (locate-library "matlab")) (dir (expand-file-name "toolbox/emacsinit.m" (file-name-directory (or mlfile ""))))) (and mlfile (file-exists-p dir))) "Add the `matlab-shell' MATLAB toolbox to the MATLAB path on startup.") (defun matlab-shell-first-prompt-fcn () "Hook run when the first prompt is seen. Sends commands to the MATLAB shell to initialize the MATLAB process." ;; Don't do this again (remove-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-first-prompt-fcn) ;; Init this session of MATLAB. (if matlab-shell-use-emacs-toolbox ;; Use our local toolbox directory. (let* ((path (expand-file-name "toolbox" (file-name-directory (locate-library "matlab")))) (initcmd (expand-file-name "emacsinit" path)) (nsa (if matlab-shell-autostart-netshell "emacs.set('netshell', true);" "")) (ecc (matlab-shell--get-emacsclient-command)) (ecca (if ecc (format "emacs.set('clientcmd', '%s');" ecc) "")) (args (list nsa ecca)) (cmd (format "run('%s');%s" initcmd (apply 'concat args)))) (matlab-shell-send-command (string-replace (expand-file-name "~/") "~/" cmd)) ) ;; Setup is misconfigured - we need emacsinit because it tells us how to debug (error "Unable to initialize matlab, emacsinit.m and other files missing")) ;; Init any user commands (if matlab-custom-startup-command ;; Wait for next prompt, then send. (add-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-user-startup-fcn) ;; No user startup command? Wait for final prompt to signal we are done. (add-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-second-prompt-fcn) )) (defun matlab-shell-user-startup-fcn () "Hook run on second prompt to run user specified startup functions." ;; Remove ourselves (remove-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-user-startup-fcn) ;; Run user's startup (matlab-shell-send-command (concat matlab-custom-startup-command "")) ;; Wait for the next prompt to appear and finally set that we are ready. (add-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-second-prompt-fcn) ) (defun matlab-shell-second-prompt-fcn () "Hook to run when the first prompt AFTER the call to emacsinit." (remove-hook 'matlab-shell-prompt-appears-hook #'matlab-shell-second-prompt-fcn) (setq matlab-prompt-seen t)) ;;; OUTPUT Capture ;; (declare-function matlab-shell-help-mode "matlab-topic") (defun matlab-shell-process-capture-text (str) "Process text found between and . Text is found in `matlab-shell-wrapper-filter', and then this function is called before removing text from the output stream. This function detects the type of output (an eval, or output to buffer) and then processes it." (let ((start nil) (end nil) (buffname "*MATLAB Output*") (text nil) (showbuff nil) ) (save-match-data ;; Strip start anchor. (unless (string-match (regexp-quote matlab-shell-capturetext-start-text) str) (error "Capture text failed to provide start token. [%s]" str)) (setq text (substring str (match-end 0))) ;; Strip and ID the directive (eval or buffer name) (when (and (string-match "[ ]*(\\([^)\n]+\\))" text) (= (match-beginning 0) 0)) (setq buffname (match-string 1 text)) (setq text (substring text (match-end 0))) ) ;; Strip the tail. (if (string-match (regexp-quote matlab-shell-capturetext-end-text) text) (setq text (substring text 0 (match-beginning 0))) (error "Capture text failed to provide needed end token. [%s]" text)) ;; Act on the content (if (string= buffname "eval") ;; The desire is to evaluate some Emacs Lisp code instead of ;; capture output to display in Emacs. (let ((evalforms (read text))) ;; Evaluate some forms (condition-case nil (eval evalforms) (error (message "Failed to evaluate forms from MATLAB: \"%S\"" evalforms)))) ;; Generate the buffer and contents (with-current-buffer (get-buffer-create buffname) (setq buffer-read-only nil) ;; Clear it if not appending. (erase-buffer) (insert text) (goto-char (point-min)) (setq showbuff (current-buffer)) ) ;; Display the buffer (cond ((string-match "^\\*MATLAB Help" buffname) (with-current-buffer showbuff (matlab-shell-help-mode))) (t (with-current-buffer showbuff (view-mode)))) (display-buffer showbuff '((display-buffer-use-some-window display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t) (window-height . shrink-window-if-larger-than-buffer)))) ))) ;;; COMMANDS ;; ;; Commands for interacting with the MATLAB shell buffer (defun matlab-shell-interrupt-subjob () "Call `comint-interrupt-subjob' and flush accumulation buffer." (interactive) ;; Look at the accumulation buffer, and flush it. (setq matlab-shell-flush-accumulation-buffer t) ;; Continue on to do what comint does. (comint-interrupt-subjob) ) (defun matlab-shell-next-matching-input-from-input (n) "Get the Nth next matching input from for the command line." (interactive "p") (matlab-shell-previous-matching-input-from-input (- n))) (defun matlab-shell-previous-matching-input-from-input (n) "Get the Nth previous matching input from for the command line." (interactive "p") (end-of-line) ;; patch: Mark Histed (if (comint-after-pmark-p) (if (memq last-command '(matlab-shell-previous-matching-input-from-input matlab-shell-next-matching-input-from-input)) ;; This hack keeps the cycling working well. (let ((last-command 'comint-previous-matching-input-from-input)) (comint-next-matching-input-from-input (- n))) ;; first time. (comint-next-matching-input-from-input (- n))) ;; If somewhere else, just move around. (forward-line (- n)))) (defun matlab-shell-delete-backwards-no-prompt (&optional arg) "Delete one char backwards without destroying the matlab prompt. Optional argument ARG describes the number of chars to delete." (interactive "P") (let ((promptend (save-excursion (beginning-of-line) (if (looking-at "K?>> ") (match-end 0) (point)))) (numchars (if (integerp arg) (- arg) -1))) (if (<= promptend (+ (point) numchars)) (delete-char numchars) (error "Beginning of line")))) ;;; COMPLETION ;; ;; Request list of completions from MATLAB. ;; Support classic emacs in-place completion, or company mode if available. (defun matlab-shell-completion-list (str) "Get a list of completions from MATLAB. STR is a command substring to complete." (let* ((msbn (matlab-shell-buffer-barf-not-running)) (cmd (concat "emacsdocomplete('" str "')")) (comint-scroll-show-maximum-output nil) output (replacement-text "") (cmd-text-to-replace "") (completions nil)) (with-current-buffer msbn (if (not (matlab-on-prompt-p)) (error "MATLAB shell must be non-busy to do that")) (setq output (matlab-shell-collect-command-output cmd)) (if (not (string-match "emacs_completions_output =" output)) (error "Internal error, '%s' returned unexpected output, %s" cmd output)) (setq output (substring output (match-end 0))) (when (string-match "^'\\([^']+\\)' --> '\\([^']*\\)'" output) ;; 'CMD_TEXT_TO_REPLACE' --> 'REPLACEMENT_TEXT' ;; 'OPTION1' ;; 'OPTION2' ;; ... ;; Note, the CMD_TEXT_TO_REPLACE line is only present when there needs ;; to be replacement, e.g. imagine a command that takes glob patterns ;; >> mycmd foo*ba ;; 'foo*-bar' --> 'foo-and-bar' ;; '-or-goo' ;; '-or-too' ;; which completes to either 'foo-and-bar-or-goo' OR 'foo-and-bar-or-too'. ;; If there is only one completion that needs replacement, don't have options: ;; >> mycmd foo*ba*-too ;; 'foo*ba*-too' --> 'foo-and-bar-or-too' ;; The replacement line is not present when the completion just appends to the ;; command str, e.g. ;; >> mycmd foo-and-bar ;; '-or-goo' ;; '-or-too' (setq cmd-text-to-replace (match-string 1 output)) (setq replacement-text (match-string 2 output)) (setq output (substring output (match-end 0)))) ;; Parse the output string. (while (string-match "'" output) ;; Hack off the preceding quote (setq output (substring output (match-end 0))) (string-match "'" output) ;; we are making a completion list, so that is a list of lists. (setq completions (cons (list (substring output 0 (match-beginning 0))) completions) output (substring output (match-end 0)))) ;; Return them (list (cons 'cmd-text-to-replace cmd-text-to-replace) (cons 'replacement-text replacement-text) (cons 'completions (nreverse completions))) ))) (defun matlab-shell-get-completion-limit-pos (last-cmd completions) "Return the starting location of the common substring for completion. Used by `matlab-shell-tab' to in matching the COMPLETIONS, i.e. (substring LAST-CMD limit-pos (length last-cmd)) is the common starting substring of each completion in completions." (let ((limit-pos (length last-cmd))) (when completions (let* ((completion (car (car completions))) (i (length completion)) (chomp-num-chars nil)) (while (> i 0) (let ((part (substring completion 0 i))) (if (string-suffix-p part last-cmd) (progn (setq chomp-num-chars i) (setq i 0)) (setq i (- i 1))))) (if chomp-num-chars (setq limit-pos (- (length last-cmd) chomp-num-chars))))) limit-pos)) (defun matlab-shell-get-completion-info () "Compute completions needed for `matlab-shell-tab' and `company-matlab-shell'. Completions are computed based on the prefix on the last command prompt. No completions are provided anywhere else in the buffer." (if (or (not (= (point) (point-max))) (not (matlab-on-prompt-p))) nil ;; no completions. We can only complete when typing a command. (let ((inhibit-field-text-motion t) (last-cmd nil) (last-cmd-start-point nil) (common-substr nil) (limit-pos nil) (completions nil) (common-substr-start-pt nil) (common-substr-end-pt nil) (did-completion nil)) ;; Load last-cmd which is the command we are completing on. ;; We need to avoid altering point because when we ask for completions, we send ;; emacsdocomplete(last-cmd-quoted) to the MATLAB command window and then grab the results ;; and erase them. (save-excursion (goto-char (point-max)) (beginning-of-line) (re-search-forward comint-prompt-regexp) (setq last-cmd-start-point (point)) ;; save the old (last) command (setq last-cmd (buffer-substring (point) (matlab-point-at-eol)))) ;; Get the list of completions. ;; When obtaining completions, we can't use save-excursion because we are ;; manipulating the text in the *MATLAB* window at the point and this ;; move point-marker which causes save-excursion to move to the wrong ;; location. We need to do this before we manipulate the text in the ;; *MATLAB* buffer because `matlab-shell-completion-list' sends ;; emacsdocompletion('statement') to matlab and matlab produces output in ;; the *MATLAB* buffer, then `matlab-shell-completion-list' removes the ;; output from the *MATLAB* buffer. (let ((last-cmd-quoted last-cmd)) ;; Load last-cmd-quoted which the expression typed (e.g. "!mv file."). ;; Note, this has single quotes doubled up so we can ask ;; MATLAB for completions on last-cmd-quoted. (while (string-match "[^']\\('\\)\\($\\|[^']\\)" last-cmd-quoted) (setq last-cmd-quoted (replace-match "''" t t last-cmd-quoted 1))) (let* ((completion-list (matlab-shell-completion-list last-cmd-quoted)) (cmd-text-to-replace (cdr (assoc 'cmd-text-to-replace completion-list)))) (setq completions (cdr (assoc 'completions completion-list))) (when cmd-text-to-replace ;; need to alter the command to replace replacement-text with the common substring (let ((replacement-text (cdr (assoc 'replacement-text completion-list))) (last-cmd-start-len (- (length last-cmd) (length cmd-text-to-replace)))) ;; Replace the text typed in the *MATLAB* and update last-cmd (goto-char (+ last-cmd-start-point last-cmd-start-len)) (delete-region (point) (matlab-point-at-eol)) (insert replacement-text) (setq last-cmd (concat (substring last-cmd 0 last-cmd-start-len) replacement-text)) (if (not completions) (setq did-completion t)) )))) ;; Consider ;; >> ! touch foo.ext1 foo.ext2 ;; >> ! mv foo. ;; 'completions' will contain (("foo.ext1") ("foo.ext2")) and ;; common-substr will be "foo." which is used in displaying the ;; completions. The limit-pos in this case will be 5 and ;; last-cmd "! mv foo." (setq limit-pos (matlab-shell-get-completion-limit-pos last-cmd completions)) (setq common-substr (substring last-cmd limit-pos)) ;; Mark the subfield of the completion result so we can say no completions ;; if there aren't any otherwise we need to remove it. (save-excursion (goto-char (point-max)) (beginning-of-line) (re-search-forward comint-prompt-regexp) (setq common-substr-start-pt (+ (point) limit-pos)) (setq common-substr-end-pt (matlab-point-at-eol)) (if (and (eq (length completions) 1) (string-equal (buffer-substring-no-properties common-substr-start-pt common-substr-end-pt) (car (car completions)))) (setq completions nil))) ;; force display of "No completions" ;; Result (list (cons 'last-cmd last-cmd) (cons 'common-substr common-substr) (cons 'limit-pos limit-pos) (cons 'completions completions) (cons 'common-substr-start-pt common-substr-start-pt) (cons 'common-substr-end-pt common-substr-end-pt) (cons 'did-completion did-completion) )))) (defun matlab-shell-c-tab () "Send [TAB] to the currently running matlab process and retrieve completions." (interactive) (let ((matlab-shell-tab-company-available nil)) (matlab-shell-tab))) ;; matlab-shell-tab, ;; This sends the command text at the prompt to emacsdocomplete.m which returns a list ;; of possible completions. To do this we use comint to 'type' emacsdocomplete(command) ;; in the *MATLAB* buffer and then we extract the result. The emacsdocomplete returns ;; a list of possible completions of the command where each completion starts with ;; zero or more characters of the command "suffix". For example, give a directory ;; containing files, foo.ext1 and foo.ext2, ;; >> ! mv foo. ;; will call emacsdocomplete('! mv foo.') which returns (("foo.ext1") ("foo.ext2")) ;; where we have four characters "foo." matching the suffix of command. There can ;; be zero suffix match as in ;; >> !ls /usr/ ;; which on Linux returns a long list of items e.g. (("bin", "include", ...)) ;; ;; ;; Test cases (using R2016b): ;; ;; >> h=figure; ;; >> h.Num Should show Number ;; >> h.Number Should show Number and NumberTitle ;; >> h.Num Should do same. The extra white space shouldn't mess things up. ;; ;; >> h.NumberTitle Should display message "No completions" ;; >> set(h,' Should display a long list ;; type P Should narrow to Parent, Position, ... ;; ;; >> !touch file.ext Assuming no other file.* names in current directory. ;; >> !mv file. Should complete to file.ext ;; >> !mv file.ext Should do nothing ;; ;; >> !/usr/b Should complete to /usr/bin ;; >> !/usr/bin/cpp-4. Should complete to something like /usr/bin/cpp-4.9 ;; ;; >> ls /usr/include/byteswap. Some file with a '.' should complete correctly ;; ;; >> !touch foo.ext1 foo.ext2 ;; >> ! mv foo. Should give options foo.ext1 foo.ext2 ;; >> ! mv foo.ext1 Should say no completions ;; >> ! mv foo.ext1 Should say no completions (note the space before the TAB) ;; ;; >> vdp ;; >> get_param('vdp','Pos Should show several completions (defun matlab-shell-tab () "Perform completions at the `matlab-shell' command prompt. By default, uses `matlab-shell' toolbox command emacsdocomplete.m to get completions. If `matlab-shell-ask-MATLAB-for-completions' is nil, then use `comint-dynamic-complete-filename' instead. If `matlab-shell-tab-use-company' is non-nil, and if `company-mode' is installed, then use company to display completions in a popup window." (interactive) (cond ;; If we aren't supposed to ask MATLAB for completions, then use ;; comint basics. ((not matlab-shell-ask-MATLAB-for-completions) (call-interactively 'comint-dynamic-complete-filename)) ;; If company mode is available and we ask for it, use that. ((and matlab-shell-tab-company-available matlab-shell-tab-use-company company-mode) ;; We don't add to company-backends because we bind TAB to matlab-shell-tab ;; which means completions must be explicitly requested. The default ;; company-complete tries to complete as you type which doesn't work ;; so well because it can take MATLAB a bit to compute completions. (call-interactively 'company-matlab-shell)) ;; Starting in Emacs 23, completion-in-region has everything we need for basic ;; in-buffer completion ((fboundp 'completion-in-region) (matlab-shell-do-completion-light)) ;; Old school completion (t (matlab-shell-do-completion)) )) (defun matlab-shell-do-completion-light () "Perform completion using `completion-in-region'." (let* ((inhibit-field-text-motion t) (completion-info (matlab-shell-get-completion-info)) (completions (cdr (assoc 'completions completion-info))) (common-substr-start-pt (cdr (assoc 'common-substr-start-pt completion-info))) (common-substr-end-pt (cdr (assoc 'common-substr-end-pt completion-info))) ) (completion-in-region common-substr-start-pt common-substr-end-pt completions))) (defun matlab-shell-do-completion () "Perform completion using Emacs buffers. This should work in version before `completion-in-region' was available." (let* ((inhibit-field-text-motion t) (completion-info (matlab-shell-get-completion-info)) ;;(last-cmd (cdr (assoc 'last-cmd completion-info))) (common-substr (cdr (assoc 'common-substr completion-info))) (limit-pos (cdr (assoc 'limit-pos completion-info))) (completions (cdr (assoc 'completions completion-info))) (common-substr-start-pt (cdr (assoc 'common-substr-start-pt completion-info))) (common-substr-end-pt (cdr (assoc 'common-substr-end-pt completion-info))) (did-completion (cdr (assoc 'did-completion completion-info)))) (when (not did-completion) ;; Whack the old command 'substring' that is starting part of the ;; completions so we can insert it back later (delete-region common-substr-start-pt common-substr-end-pt) (goto-char (point-max)) ;; Process the completions (if (eq (length completions) 1) ;; If there is only one, then there is an obvious thing to do. (progn (insert (car (car completions))) ;; kill completions buffer if still visible (matlab-shell-tab-hide-completions)) ;; else handle multiple completions (let ((try nil)) (setq try (try-completion common-substr completions)) ;; Insert in a good completion. (cond ((or (eq try nil) (eq try t) (and (stringp try) (string= try common-substr))) (insert common-substr) (let ((cbuff (get-buffer-create "*Completions*"))) (with-output-to-temp-buffer cbuff (matlab-display-completion-list (mapcar 'car completions) common-substr)) (display-buffer cbuff '((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer)) ) )) ((stringp try) (insert try) (matlab-shell-tab-hide-completions)) (t (insert common-substr)))) )))) (defun matlab-shell-tab-hide-completions () "Hide any completion windows for `matlab-shell-tab'." (let ((bw (get-buffer-window "*Completions*"))) (when bw (quit-window nil (get-buffer-window "*Completions*"))))) ;;; Find Files ;; ;; Finding Files with MATLAB shell. ;; Originally for use with semantic-matlab, but now used in more places. (defun matlab-shell-which-fcn (fcn) "Get the location of FCN's M file. Returns an alist: ( LOCATION . BUILTINFLAG ) LOCATION is a string indicating where it is, and BUILTINFLAG is non-nil if FCN is a builtin." (save-excursion (let* ((msbn (matlab-shell-buffer-barf-not-running)) (cmd (format "disp(which('%s'))" fcn)) (comint-scroll-show-maximum-output nil) output builtin ) (set-buffer msbn) (goto-char (point-max)) (if (not (matlab-on-prompt-p)) (error "MATLAB shell must be non-busy to do that")) (setq output (matlab-shell-collect-command-output cmd)) ;; BUILT-IN (cond ((string-match "built-in (\\([^)]+\\))" output) (cons (concat (substring output (match-beginning 1) (match-end 1)) ".m") t)) ;; Error ((string-match "not found" output) nil) ;; JUST AN M FILE (t (string-match "$" output) (cons (substring output 0 (match-beginning 0)) nil)))))) (defun matlab-shell-locate-fcn (fcn) "Run \"which FCN\" in the `matlab-shell', then open the file." (interactive (list (let ((default (matlab-read-word-at-point))) (if (and default (not (equal default ""))) (let ((s (read-string (concat "MATLAB locate fcn (default " default "): ")))) (if (string= s "") default s)) (read-string "MATLAB locate fcn: "))))) (let ((file (matlab-shell-which-fcn fcn))) (if file (find-file (car file)) (error "Command which('%s') returned empty" fcn)))) (defvar matlab-shell-matlabroot-run nil "Cache of MATLABROOT in this shell.") (make-variable-buffer-local 'matlab-shell-matlabroot-run) (defun matlab-shell-matlabroot () "Get the location of this shell's root. Returns a string path to the root of the executing MATLAB." (save-excursion (let* ((msbn (matlab-shell-buffer-barf-not-running)) (cmd "disp(matlabroot)") (comint-scroll-show-maximum-output nil) output builtin ) (set-buffer msbn) (goto-char (point-max)) (if matlab-shell-matlabroot-run matlab-shell-matlabroot-run ;; If we haven't cached it, calculate it now. (if (not (matlab-on-prompt-p)) (error "MATLAB shell must be non-busy to do that")) (setq output (matlab-shell-collect-command-output cmd)) (string-match "$" output) (setq matlab-shell-matlabroot-run (substring output 0 (match-beginning 0))))))) ;;; MATLAB Shell Commands ===================================================== ;; ;; These commands will use matlab-shell as a utility, capture and display output. (defun matlab-read-word-at-point () "Get the word closest to point, but do not change position. Has a preference for looking backward when not directly on a symbol. Snatched and hacked from dired-x.el" (let ((word-chars "a-zA-Z0-9_") (bol (matlab-point-at-bol)) (eol (matlab-point-at-eol)) start) (save-excursion ;; First see if just past a word. (if (looking-at (concat "[" word-chars "]")) nil (skip-chars-backward (concat "^" word-chars "{}()\[\]") bol) (if (not (bobp)) (backward-char 1))) (if (numberp (string-match (concat "[" word-chars "]") (char-to-string (following-char)))) (progn (skip-chars-backward word-chars bol) (setq start (point)) (skip-chars-forward word-chars eol)) (setq start (point))) ; If not found, return empty string (buffer-substring start (point))))) (defun matlab-read-line-at-point () "Get the line under point, if command line." (if (eq major-mode 'matlab-shell-mode) (save-excursion (let ((inhibit-field-text-motion t)) (beginning-of-line) (if (not (looking-at (concat comint-prompt-regexp))) "" (search-forward-regexp comint-prompt-regexp) (buffer-substring (point) (matlab-point-at-eol))))) (save-excursion (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." (let ((count 0) (start 0)) (while (string-match "^.+$" str start) (setq count (1+ count) start (match-end 0))) count)) (declare-function matlab-shell-help-mode "matlab-topic") (defun matlab-output-to-temp-buffer (buffer output) "Print output to temp buffer, or a message if empty string. BUFFER is the buffer to output to, and OUTPUT is the text to insert." (let ((lines-found (matlab-non-empty-lines-in-string output))) (cond ((= lines-found 0) (message "(MATLAB command completed with no output)")) ((= lines-found 1) (string-match "^.+$" output) (message (substring output (match-beginning 0)(match-end 0)))) (t (with-output-to-temp-buffer buffer (princ output)) (with-current-buffer buffer (matlab-shell-help-mode)))))) (defun matlab-shell-run-command (command) "Run COMMAND and display result in a buffer. This command requires an active MATLAB shell." (interactive (list (read-from-minibuffer "MATLAB command line: " (cons (matlab-read-line-at-point) 0)))) (let ((doc (matlab-shell-collect-command-output command))) (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) (defun matlab-shell-describe-variable (variable) "Get the contents of VARIABLE and display them in a buffer. This uses the WHOS (MATLAB 5) command to find viable commands. This command requires an active MATLAB shell." (interactive (list (read-from-minibuffer "MATLAB variable: " (cons (matlab-read-word-at-point) 0)))) (let ((doc (matlab-shell-collect-command-output (concat "whos " variable)))) (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) (defun matlab-shell-describe-command (command) "Describe COMMAND textually by fetching it's doc from the MATLAB shell. This uses the lookfor command to find viable commands. This command requires an active MATLAB shell." (interactive (let ((fn (matlab-function-called-at-point)) val) (setq val (read-string (if fn (format "Describe function (default %s): " fn) "Describe function: "))) (if (string= val "") (list fn) (list val)))) (let ((doc (matlab-shell-collect-command-output (concat "help -emacs " command)))) (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) (defun matlab-shell-apropos (matlabregex) "Look for any active commands in MATLAB matching MATLABREGEX. This uses the lookfor command to find viable commands." (interactive (list (read-from-minibuffer "MATLAB command subexpression: " (cons (matlab-read-word-at-point) 0)))) (let ((ap (matlab-shell-collect-command-output (concat "lookfor " matlabregex)))) (matlab-output-to-temp-buffer "*MATLAB Apropos*" ap))) (defun matlab-on-prompt-p () "Return t if we MATLAB can accept input." (save-excursion (let ((inhibit-field-text-motion t)) (goto-char (point-max)) (beginning-of-line) (looking-at comint-prompt-regexp)))) (defun matlab-on-empty-prompt-p () "Return t if we MATLAB is on an empty prompt." (with-current-buffer (matlab-shell-active-p) (let ((inhibit-field-text-motion t)) (goto-char (point-max)) (beginning-of-line) (looking-at (concat comint-prompt-regexp "\\s-*$"))))) (defun matlab-on-debug-prompt-p () "Return t if we MATLAB is on an debug prompt." (with-current-buffer (matlab-shell-active-p) (let ((inhibit-field-text-motion t)) (goto-char (point-max)) (beginning-of-line) (looking-at (concat "K>>\\s-*"))))) (defun matlab-shell-buffer-barf-not-running () "Return a running MATLAB buffer iff it is currently active." (or (matlab-shell-active-p) (error "You need to run the command `matlab-shell' to do that!"))) (defun matlab-shell-collect-command-output (command) "If there is a MATLAB shell, run the MATLAB COMMAND and return it's output. It's output is returned as a string with no face properties. The text output of the command is removed from the MATLAB buffer so there will be no indication that it ran." (let ((msbn (matlab-shell-buffer-barf-not-running)) (matlab-shell-suppress-prompt-hooks t)) ;; We are unable to use save-excursion to save point position because we are ;; manipulating the *MATLAB* buffer by erasing the current text typed at the ;; MATLAB prompt (where point is) and then we send command to MATLAB and ;; grab the result. After this we erase the output from command and then ;; restore the current text at the MATLAB prompt and move to start-point. ;; Note, save-excursion works by tracking `point-marker' and when you manipulate ;; the text at point, `point-marker' moves causing save-excursion to move ;; the point in to a location we don't want. See: ;; http://emacs.stackexchange.com/questions/7574/why-save-excursion-doesnt-save-point-position ;; Ideally there would be some way to prevent the *MATLAB* buffer from refreshing ;; as we are interacting with it, but I couldn't figure out a way to do that. (with-current-buffer msbn (save-window-excursion (let ((pos nil) (str nil) (lastcmd) (inhibit-field-text-motion t) (start-point (point))) (if (not (matlab-on-prompt-p)) (error "MATLAB shell must be non-busy to do that")) ;; Save the old command (goto-char (point-max)) (beginning-of-line) (re-search-forward comint-prompt-regexp) ;; Backup if there are extra spaces. To see why, try tab completion on command with ;; leading spaces, e.g. ;; >> h=figure; ;; >> h.Num (re-search-backward ">") (forward-char 2) (setq lastcmd (buffer-substring (point) (matlab-point-at-eol))) (delete-region (point) (matlab-point-at-eol)) ;; We are done error checking, run the command. (setq pos (point)) (let ((output-start-char ;; We didn't get enough output until we are past the starting point. ;; Starting point depends on if we echo or not. (if matlab-shell-echoes (+ pos 1 (string-width command)) ; 1 is newline pos)) (notimeout t) ) ;; Note, comint-simple-send in emacs 24.4 appends a newline and code below assumes ;; one prompt indicates command completed, so don't append a newline. (comint-simple-send (get-buffer-process (current-buffer)) command) ;; Wait for the command to finish, by looking for new prompt. (goto-char (point-max)) ;; Turn on C-g by using with-local-quit. This is needed to prevent message: ;; "Blocking call to accept-process-output with quit inhibited!! [115 times]" ;; when using `company-matlab-shell' for TAB completions. (with-local-quit (while (or (>= output-start-char (point)) (not (matlab-on-empty-prompt-p)) notimeout) (setq notimeout (accept-process-output (get-buffer-process (current-buffer)) .1)) (goto-char (point-max)))) ;; Get result of command into str (goto-char pos) (setq str (buffer-substring-no-properties (save-excursion (goto-char output-start-char) (point)) (save-excursion (goto-char (point-max)) (beginning-of-line) (point)))) ) ;; delete the result of command (delete-region pos (point-max)) ;; restore contents of buffer so it looks like nothing happened. (insert lastcmd) (goto-char start-point) ;; return result 'string' from executing MATLAB command str))))) (defun matlab-shell-send-command (command) "Send COMMAND to a MATLAB process. If there is a `matlab-shell', send it to the command prompt. If there is only a `matlab-netshell', send it to the netshell." (if (matlab-shell-active-p) (with-current-buffer (matlab-shell-active-p) (matlab-shell-send-string (concat command "\n"))) ;; As a backup, use netshell. (matlab-netshell-eval command))) (defun matlab-shell-send-string (string) "Send STRING to the currently running matlab process." (if (not matlab-shell-echoes) (let ((proc (get-buffer-process (current-buffer)))) (goto-char (point-max)) (insert string) (set-marker (process-mark proc) (point)))) (when matlab-shell-io-testing (message "<--[%s]" string)) (comint-send-string (get-buffer-process (current-buffer)) string)) (defun matlab-url-at (p) "Return the matlab-url overlay at P, or nil." (let ((url nil) (o (matlab-overlays-at p))) (while (and o (not url)) (setq url (matlab-overlay-get (car o) 'matlab-url) o (cdr o))) url)) (defun matlab-url-stack-top-at (p) "Return the matlab-url overlay at P, or nil." (let ((url nil) (o (matlab-overlays-at p))) (while (and o (not url)) (setq url (or (matlab-overlay-get (car o) 'first-in-error-stack) (matlab-overlay-get (car o) 'matlab-url)) o (cdr o))) url)) (defun matlab-shell-previous-matlab-url (&optional stacktop) "Find a previous occurrence of an overlay with a MATLAB URL. If STACKTOP is non-nil, then also get the top of some stack, which didn't show up in reverse order." (save-excursion (let ((url nil) (o nil) (p (point))) (while (and (not url) (setq p (matlab-previous-overlay-change p)) (not (eq p (point-min)))) (setq url (if stacktop (matlab-url-stack-top-at p) (matlab-url-at p)))) url))) ;; (matlab-shell-mref-to-filename "eltest.utils.testme>localfcn") (defun matlab-shell-class-mref-to-file (mref &optional fcn-p) "Convert a class like reference MREF to a file name. Optional FCN-P indicates specifies to force treating as a function." (let* ((LF (split-string mref ">")) (S (split-string (car LF) "\\.")) (L (last S)) (ans nil)) (if (member L '("mlx" "m")) nil ;; Not a . from a .m file, probably a class ?? (while S (when (and (= (length S) 1) (not fcn-p)) ;; Is there is a method? strip it off. (let ((meth (split-string (car S) "/"))) (setq S (list (car meth))))) ;; Append the parts together. (setq ans (concat ans (if (> (length S) 1) "+" (unless fcn-p (concat "@" (car S) "/"))) (car S))) (setq S (cdr S)) (if S (setq ans (concat ans "/")) (setq ans (concat ans ".m"))) )) ans)) (defun matlab-shell-mref-which-fcn (ref) "Try to run 'which' on REF to find actual file location. If the MATLAB shell isn't ready to run a which command, skip and return nil." (when (not matlab-shell-in-process-filter) (save-excursion (let* ((msbn (matlab-shell-buffer-barf-not-running))) (set-buffer msbn) (goto-char (point-max)) (if (and (matlab-on-prompt-p) (not matlab-shell-cco-testing)) (matlab-shell-which-fcn ref) nil))))) (defvar matlab-shell-mref-converters '( ;; Does it work as is? (lambda (mref) mref) ;; p files (lambda (mref) (when (string-match "\\.\\(p\\)$" mref) (replace-match "m" nil t mref 1))) ;; Function name, no extension. (lambda (mref) (when (not (string-match "\\.m$" mref)) (concat mref ".m"))) ;; Methods in a class (lambda (mref) (when (string-match "\\." mref) (matlab-shell-class-mref-to-file mref))) ;; A function in a package (lambda (mref) (when (string-match "\\." mref) (matlab-shell-class-mref-to-file mref t))) ;; Copied from old code, not sure what it matches. (lambda (mref) (when (string-match ">" mref) (concat (substring fileref 0 (match-beginning 0)) ".m"))) ;; Ask matlab where it came from. Keep last b/c expensive, or won't ;; work if ML is busy. (lambda (mref) (car (matlab-shell-mref-which-fcn mref))) ) "List of converters to convert MATLAB file references into a filename. Each element is a function that accepts a file ref, and returns a file name, or nil if no conversion done.") ;; (matlab-shell-mref-to-filename "eltest.utils.testme>localfcn") (defun matlab-shell-mref-to-filename (fileref) "Convert the MATLAB file reference FILEREF into an actual file name. MATLAB can refer to functions on the path by a short name, or by a .p extension, and a host of different ways. Convert this reference into something Emacs can load." (interactive "sFileref: ") (with-current-buffer (matlab-shell-active-p) (let ((C matlab-shell-mref-converters) (ans nil)) (while (and C (not ans)) (let ((tmp (funcall (car C) fileref))) (when (and tmp (file-exists-p tmp)) (setq ans tmp)) ) (setq C (cdr C))) (when (called-interactively-p 'any) (message "Found: %S" ans)) ans))) (defun matlab-find-other-window-file-line-column (ef el ec &optional debug) "Find file EF in other window and to go line EL and 1-basec column EC. If DEBUG is non-nil, then setup GUD debugging features." (let ((ef-converted (matlab-shell-mref-to-filename ef))) (when (not ef-converted) (error "Failed to translate %s into a filename" ef)) (find-file-other-window ef-converted) (goto-char (point-min)) (forward-line (1- (string-to-number el))) (when debug (setq mlgud-last-frame (cons (buffer-file-name) (string-to-number el))) (mlgud-display-frame)) (setq ec (string-to-number ec)) (if (> ec 0) (forward-char (1- ec))))) ;; TODO: No callers use DEBUG input Remove? (defun matlab-find-other-window-via-url (url &optional debug) "Find other window using matlab URL and optionally set DEBUG cursor." (cond ((string-match "^error:\\(.*\\),\\([0-9]+\\),\\([0-9]+\\)$" url) (let ((ef (substring url (match-beginning 1) (match-end 1))) (el (substring url (match-beginning 2) (match-end 2))) (ec (substring url (match-beginning 3) (match-end 3)))) (matlab-find-other-window-file-line-column ef el ec debug))) ((string-match "opentoline('\\([^']+\\)',\\([0-9]+\\),\\([0-9]+\\))" url) (let ((ef (substring url (match-beginning 1) (match-end 1))) (el (substring url (match-beginning 2) (match-end 2))) (ec (substring url (match-beginning 3) (match-end 3)))) (matlab-find-other-window-file-line-column ef el ec debug))) ((string-match "^matlab:*\\(.*\\)$" url) (process-send-string (get-buffer-process mlgud-comint-buffer) (concat (substring url (match-beginning 1) (match-end 1)) "\n"))))) (defun matlab-shell-last-error () "In the MATLAB interactive buffer, find the last MATLAB error, and go there. To reference old errors, put the cursor just after the error text." (interactive) (catch 'done (let ((url (matlab-shell-previous-matlab-url t))) (if url (progn (matlab-find-other-window-via-url url) (throw 'done nil)) (save-excursion (end-of-line) ;; In case we are before the line number 1998/06/05 16:54sk (let ((err (matlab-shell-scan-for-error (point-min)))) (when (not err) (error "No errors found!")) (let ((ef (nth 2 err)) (el (nth 3 err)) (ec (or (nth 4 err) "0"))) (matlab-find-other-window-file-line-column ef el ec)))))))) (defun matlab-shell-html-click (e) "Go to the error at the location of event E." (interactive "e") (mouse-set-point e) (matlab-shell-html-go)) (defun matlab-shell-html-go () "Go to the error at the location `point'." (interactive) (let ((url (matlab-url-at (point)))) (if url (matlab-find-other-window-via-url url)))) (defun matlab-shell-dbstop-error () "Stop on errors." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "dbstop if error\n")) (defun matlab-shell-dbclear-error () "Don't stop on errors." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "dbclear if error\n")) (defun matlab-shell-demos () "MATLAB demos." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "demo\n")) (defun matlab-shell-close-figures () "Close any open figures." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "close all\n")) (defun matlab-shell-close-current-figure () "Close current figure." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "delete(gcf)\n")) (defun matlab-shell-sync-buffer-directory () "Sync matlab-shell `default-directory' with MATLAB's pwd. These will differ when MATLAB code directory without notifying Emacs." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "emacscd%%\n")) (defun matlab-shell-exit () "Exit MATLAB shell." (interactive) (comint-send-string (get-buffer-process (current-buffer)) "exit\n") (kill-buffer nil)) ;;; MATLAB mode Shell commands ================================================ ;; ;; These commands are provided in MATLAB code buffers to interact with ;; the shell. (defun matlab-show-matlab-shell-buffer () "Switch to the buffer containing the matlab process." (interactive) (let ((msbn (concat "*" matlab-shell-buffer-name "*"))) (if (get-buffer msbn) (switch-to-buffer-other-window msbn) (message "There is not an active MATLAB process.")))) (defvar matlab-shell-save-and-go-history '("()") "Keep track of parameters passed to the MATLAB shell.") (defvar matlab-shell-save-and-go-command nil "Command to use for `matlab-shell-save-and-go' instead of current buffer. This command will override the default computed command if non-nil. The command will be run in the shell's current directory without checks, so you will need to make sure MATLAB's pwd is correct. It is recommended you use directory-local or buffer-local variable settings to control this.") (make-variable-buffer-local 'matlab-shell-save-and-go-command) ;; Marking as SAFE b/c we will ask to use this before doing so. (put 'matlab-shell-save-and-go-command 'safe-local-variable #'stringp) (defvar matlab-shell-save-and-go-command-enabled nil "Remember if it is safe to use `matlab-shell-save-and-go-command' in this buffer.") (make-variable-buffer-local 'matlab-shell-save-and-go-command-enabled) (put 'matlab-shell-save-and-go-command 'risky-local-variable t) (defun matlab-shell-set-save-and-go-command (command) "Set `matlab-shell-save-and-go-command' for any file in the current directory. Value is set to COMMAND." (interactive (list (read-string "sCommand: " (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))))) (when (not (eq major-mode 'matlab-mode)) (error "Cannot set save-and-go command for buffer in %s" major-mode)) (add-dir-local-variable 'matlab-mode 'matlab-shell-save-and-go-command command)) (defun matlab-shell-add-to-input-history (string) "Add STRING to the input-ring and run `comint-input-filter-functions' on it. Similar to `comint-send-input'." (if (and (funcall comint-input-filter string) (or (null comint-input-ignoredups) (not (ring-p comint-input-ring)) (ring-empty-p comint-input-ring) (not (string-equal (ring-ref comint-input-ring 0) string)))) (ring-insert comint-input-ring string)) (run-hook-with-args 'comint-input-filter-functions (concat string "\n")) (if (boundp 'comint-save-input-ring-index);only bound in GNU emacs (setq comint-save-input-ring-index comint-input-ring-index)) (setq comint-input-ring-index nil)) (defun matlab-shell-save-and-go () "Save this M file, and evaluate it in a MATLAB shell." (interactive) (if (not (eq major-mode 'matlab-mode)) (error "Save and go is only useful in a MATLAB buffer!")) (if (not (buffer-file-name (current-buffer))) (call-interactively 'write-file)) (let* ((fn-name (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) (msbn (concat "*" matlab-shell-buffer-name "*")) (do-local t)) (when matlab-shell-save-and-go-command ;; If an override command is set, run that instead of this file. (let* ((cmd matlab-shell-save-and-go-command) (use (or matlab-shell-save-and-go-command-enabled (string= cmd fn-name) (y-or-n-p (format "Run \"%s\" instead of %s? " cmd fn-name))))) (if (not use) ;; Revert to old behavior. nil ;; Else, use it. (setq do-local nil matlab-shell-save-and-go-command-enabled t) ;; No buffer? No net connection? Make a shell! (if (and (not (get-buffer msbn)) (not (matlab-netshell-active-p))) (matlab-shell)) (when (get-buffer msbn) ;; Ok, now fun the function in the matlab shell (if (get-buffer-window msbn t) (select-window (get-buffer-window msbn t)) (switch-to-buffer-other-window (concat "*" matlab-shell-buffer-name "*"))) (goto-char (point-max))) (matlab-shell-send-command (concat cmd "\n")) ))) (when do-local ;; else - try to make something up to run this specific command. (let* ((dir (expand-file-name (file-name-directory buffer-file-name))) (edir dir) (change-cd matlab-change-current-directory) (param "")) (save-buffer) ;; Do we need parameters? (if (save-excursion (goto-char (point-min)) (end-of-line) (forward-sexp -1) (looking-at "([a-zA-Z]")) (setq param (read-string "Parameters: " (car matlab-shell-save-and-go-history) 'matlab-shell-save-and-go-history))) ;; No buffer? No net connection? Make a shell! (if (and (not (get-buffer msbn)) (not (matlab-netshell-active-p))) (matlab-shell)) (when (get-buffer msbn) ;; Ok, now fun the function in the matlab shell (if (get-buffer-window msbn t) (select-window (get-buffer-window msbn t)) (switch-to-buffer-other-window (concat "*" matlab-shell-buffer-name "*"))) (goto-char (point-max))) ;; Fixup DIR to be a valid MATLAB command (mapc (lambda (e) (while (string-match (car e) dir) (setq dir (replace-match (format "', char(%s), '" (cdr e)) t t dir)))) '(("ô" . "244") ("é" . "233") ("è" . "232") ("à" . "224"))) ;; change current directory? - only w/ matlab-shell active. (if (and change-cd (get-buffer msbn)) (progn (when (not (string= dir default-directory)) (matlab-shell-send-command (concat "emacscd(['" dir "'])"))) (let ((cmd (concat fn-name " " param))) (matlab-shell-add-to-input-history cmd) (matlab-shell-send-string (concat cmd "\n")) )) ;; If not changing dir, maybe we need to use 'run' command instead? (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))) )))) ;;; Running buffer subset ;; ;; Run some subset of the buffer in matlab-shell. (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-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 () "Run region from BEG to END and display result in MATLAB shell. pIf region is not active run the current line. This command requires an active MATLAB shell." (interactive) (if (and transient-mark-mode mark-active) (matlab-shell-run-region (mark) (point)) (matlab-shell-run-region (matlab-point-at-bol) (matlab-point-at-eol)))) (defun matlab-shell-run-region (beg end &optional noshow) "Run region from BEG to END and display result in MATLAB shell. If NOSHOW is non-nil, replace newlines with commas to suppress output. This command requires an active MATLAB shell." (interactive "r") (if (> beg end) (let (mid) (setq mid beg beg end end mid))) (let ((command (matlab-shell-region-command beg end noshow)) (msbn nil) (lastcmd) (inhibit-field-text-motion t)) (if (matlab-netshell-active-p) ;; Use netshell to run the command. (matlab-netshell-eval command) ;; else, send to the command line. (save-excursion (setq msbn (matlab-shell-buffer-barf-not-running)) (set-buffer msbn) (if (not (matlab-on-prompt-p)) (error "MATLAB shell must be non-busy to do that")) ;; Save the old command (beginning-of-line) (re-search-forward comint-prompt-regexp) (setq lastcmd (buffer-substring (point) (matlab-point-at-eol))) (delete-region (point) (matlab-point-at-eol)) ;; We are done error checking, run the command. (matlab-shell-send-string command) ;; Put the old command back. (insert lastcmd))) ;; Regardless of how we send it, if there is a shell buffer, show it. (setq msbn (matlab-shell-active-p)) (when msbn (set-buffer msbn) (goto-char (point-max)) (display-buffer msbn '((display-buffer-reuse-window display-buffer-at-bottom) (reusable-frames . visible) )) ))) ;;; Convert regions to runnable text ;; ;; There are two techniques. ;; Option 1: Convert the region into a single command line, suppress output, and eval. ;; Option 2: Newer emacs, use `emacsrunregion.m' to use Editor hack for running regions out of a file. ;; Option 3: Older emacs, or if buffer isn't saved in a file. Copy into a script, and run the script. (defun matlab-shell-region-command (beg end &optional noshow) "Convert the region between BEG and END into a MATLAB command. Picks between different options for running the commands. Optional argument NOSHOW specifies if we should echo the region to the command line." (cond ((eq matlab-shell-run-region-function 'auto) (let ((cnt (count-lines beg end))) (if (< cnt 2) ;; OLD WAY (matlab-shell-region->commandline beg end noshow) ;; else ;; NEW WAYS (if (file-exists-p (buffer-file-name (current-buffer))) (progn (save-buffer) (matlab-shell-region->internal beg end noshow)) ;; No file, or older emacs, run region as tmp file. (matlab-shell-region->script beg end noshow))) )) (t (funcall matlab-shell-run-region-function beg end noshow)))) (defun matlab-shell-region->commandline (beg end &optional noshow) "Convert the region between BEG and END into a MATLAB command. Squeeze out newlines. When NOSHOW is non-nil, suppress output by adding ; to commands." ;; Assume beg & end are in the right order. (let ((str (concat (buffer-substring beg end) "\n"))) ;; Remove comments (with-temp-buffer (insert str) (goto-char (point-min)) ;; Delete all the comments (while (search-forward "%" nil t) (when (not (matlab-cursor-in-string)) (delete-region (1- (point)) (matlab-point-at-eol)))) (setq str (buffer-substring-no-properties (point-min) (point-max)))) ;; Strip out blank lines (while (string-match "^\\s-*\n" str) (setq str (concat (substring str 0 (match-beginning 0)) (substring str (match-end 0))))) ;; Strip out large chunks of whitespace (while (string-match "\\s-\\s-+" str) (setq str (concat (substring str 0 (match-beginning 0)) (substring str (match-end 0))))) (when noshow ;; Remove continuations (while (string-match (concat "\\s-*" (regexp-quote matlab-elipsis-string) "\\s-*\n") str) (setq str (replace-match " " t t str))) (while (string-match "\n" str) (setq str (replace-match ", " t t str))) (setq str (concat str "\n"))) str)) (defun matlab-shell-region->internal (beg end &optional noshow) "Create a command to run the region between BEG and END. Uses internal MATLAB API to execute the code keeping breakpoints and local functions active. Optional argument NOSHOW specifies if we should echo the region to the command line." ;; Reduce end by 1 char, as that is how ML treats it (setq end (1- end)) (let ((enc-str (symbol-name buffer-file-coding-system))) (when (string-match "\\script (beg end &optional noshow) "Extract region between BEG & END into a temporary M file. The tmp file name is based on the name of the current buffer. The extracted region is unmodified from src buffer unless NOSHOW is non-nil, in which case ; are added to quiesce the buffer. Scan the extracted region for any functions that are in the original buffer,and include them. Return the name of the temporary file." (interactive "r") (require 'semantic-matlab) (let* ((start (count-lines (point-min) beg)) (len (count-lines beg end)) (stem (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) (orig (current-buffer)) (newf (concat stem "_" (number-to-string start) "_" (number-to-string len))) (bss (buffer-substring-no-properties beg end)) (buff (find-file-noselect (concat newf ".m"))) (intro "%% Automatically created temporary file created to run-region") ;; These variables are for script / fcn tracking (functions (matlab-semantic-get-local-functions-for-script (current-buffer))) ) ;; TODO : if the directory in which the current buffer is in is READ ONLY ;; we should write our tmp buffer to /tmp instead. (with-current-buffer buff (goto-char (point-min)) ;; Clean up old extracted regions. (when (looking-at intro) (delete-region (point-min) (point-max))) ;; Don't stomp on old code. (when (not (= (point-min) (point-max))) (error "Region extract to tmp file: Temp file not empty!")) (insert intro "\n\n" bss "\n%%\n") ;; Some scripts call local functions from the script. Find them ;; and copy those local scripts over. (goto-char (point-min)) (dolist (F functions) (save-excursion (when (re-search-forward (semantic-tag-name F) nil t) ;; Found, copy it in. (let ((ft (matlab-semantic-tag-text F orig))) (goto-char (point-max)) (insert "% Copy of " (semantic-tag-name F) "\n\n") (insert ft) (insert "\n%%\n")))) ) ;; Save buffer, and setup ability to run this new script. (save-buffer) ;; Flush any pending MATLAB stuff. (accept-process-output) ;; This sets us up to cleanup our file after it's done running. (add-hook 'matlab-shell-prompt-appears-hook `(lambda () (matlab-shell-cleanup-extracted-region ,(buffer-file-name buff)))) (kill-buffer) ) ;; Return the command. (concat "run('" (expand-file-name newf) "')\n"))) (defun matlab-shell-cleanup-extracted-region (fname) "Cleanup the file created when we previously extracted a region. Argument FNAME specifies if we should echo the region to the command line." (condition-case nil (delete-file fname) (error nil)) (remove-hook 'matlab-shell-prompt-appears-hook ;; The below needs to be a perfect match to the setter. `(lambda () (matlab-shell-cleanup-extracted-region ,fname))) ) (defun matlab-find-file-click (e) "Find the file clicked on with event E on the current path." (interactive "e") (mouse-set-point e) (let ((f (matlab-read-word-at-point))) (if (not f) (error "To find an M file, click on a word")) (matlab-shell-locate-fcn f))) (provide 'matlab-shell) ;;; matlab-shell.el ends here ;; LocalWords: Ludlam zappo compat comint mlgud gud defcustom nodesktop defface netshell ;; LocalWords: emacsclient commandline emacsrunregion errorscanning cco defconst defun setq Keymaps ;; LocalWords: keymap subjob kbd emacscd featurep fboundp EDU msbn pc Thx Chappaz windowid ;; LocalWords: postoutput capturetext EMACSCAP captext STARTCAP progn eol dbhot erroexamples cdr ;; LocalWords: ENDPT dolist overlaystack mref deref errortext ERRORTXT shellerror Emacsen iq nt ;; LocalWords: auth mlfile emacsinit initcmd nsa ecc ecca clientcmd EMAACSCAP buffname showbuff ;; LocalWords: evalforms Histed pmark memq promptend numchars integerp emacsdocomplete mycmd ba ;; LocalWords: nreverse emacsdocompletion byteswap stringp cbuff mapcar bw FCN's alist ;; LocalWords: BUILTINFLAG dired bol bobp numberp princ minibuffer fn matlabregex lastcmd notimeout ;; LocalWords: stacktop eltest testme localfcn LF fileref funcall ef ec basec sk nondirectory ;; LocalWords: ignoredups boundp edir sexp Fixup mapc emacsrun noshow cnt elipsis newf bss noselect ;; LocalWords: fname