MATLAB

I still use MATLAB a lot. It works quite well with emacs. A good introduction is here.

(use-package matlab
  :demand
  :ensure matlab-mode
  :if
  <<do.matlab/binary>>
  :bind
  <<do.matlab/keys>>
  :mode ("\\.m$" . matlab-mode)
  :init
  <<do.matlab/run-matlab>>
  <<do.matlab/settings>>
  <<do.matlab/alias>>
  <<do.matlab/shell-hooks>>
  <<do.matlab/hooks>>
  (require 'mlint)
  <<do.matlab/mlint-settings>>
  <<do.matlab/mlint-faces>>
  <<do.matlab/mlint-post-command>>)

Find the binary

(cond ((boundp 'matlab-shell-command) t)
      ((executable-find "matlab")
       (setq matlab-shell-command (executable-find "matlab")) t)
      (t nil))

General settings

(setq matlab-indent-function t)
(setq matlab-shell-enable-gud-flag t)
(setq matlab-shell-command-switches '("-nodesktop" "-nosplash"))
(setq-default matlab-function t)
(setq-default matlab-highlight-cross-function-variables t)
(setq-default matlab-functions-have-end t)
(setq-default matlab-fill-code nil)
(setq-default matlab-vers-on-startup nil)
(when (boundp 'company-dabbrev-code-modes)
  (add-to-list 'company-dabbrev-code-modes 'matlab-mode)
  (add-to-list 'company-dabbrev-code-modes 'matlab-shell-mode))

Running MATLAB

MATLAB does not play nice with my shell of choice, fish. It looks like MATLAB uses shell commands internally that assume a POSIX-compliant shell. This can be fixed by setting the environment variable $SHELL before launching MATLAB. $SHELL gets passed on to MATLAB from emacs. We need to therefore make sure that emacs passes /bin/bash as $SHELL to MATLAB.

(defun run-matlab ()
  "Execute MATLAB-SHELL with POSIX-compliant SHELL env variable"
  (interactive)
  (let ((posix-shell-name "/bin/bash")
        (original-shell-env-var (getenv "SHELL")))
    ;; need to disable DRI3 according to red hat bug #1357571
    ;; (setenv "LIBGL_DRI3_DISABLE" "true") ;; -- seems to have been worked out now
    ;; if $SHELL is bash, just launch matlab
    (if (string= original-shell-env-var posix-shell-name)
        (matlab-shell)
      ;; else set the correct $SHELL, launch matlab, and revert changes
      (setenv "SHELL" posix-shell-name)
      (matlab-shell)
      (setenv "SHELL" original-shell-env-var))))

(defun run-matlab-debug ()
  (interactive)
  (let ((matlab-shell-command-switches '("-nodesktop" "-nosplash" "-nojvm" "-Dgdb")))
    (run-matlab)))

Aliases

We also want to have a legacy alias to launch matlab-shell and to run a line MATLAB code.

(defalias 'mshell 'run-matlab)
(defalias 'mrun 'matlab-shell-run-region-or-line)

Keybindings

(:map matlab-mode-map
      ("C-c C-c" .  matlab-shell-run-region-or-line))

mlint

This section configures mlint to check my MATLAB syntax after every save. We first need to point it at the right binary and set some general settings.

(if (boundp 'do.matlab/mlint-program)
    (setq mlint-program do.matlab/mlint-program)
  (setq mlint-program (executable-find "mlint")))
(setq-default matlab-show-mlint-warnings t)
(setq-default mlint-verbose nil)

Make the mlint warning faces a little more palatable.

(set-face-attribute 'linemark-stop-face nil
        :background 'unspecified
        :underline '(:color "red3" :style wave))
(set-face-attribute 'linemark-caution-face nil
        :background 'unspecified
        :underline '(:color "yellow4" :style wave))
(set-face-attribute 'linemark-go-face nil
        :background 'unspecified
        :underline '(:color "green4" :style wave))
(set-face-attribute 'linemark-funny-face nil
        :background 'unspecified
        :underline '(:color "blue3" :style wave))

In order to try to make mlint behave a little more like flycheck, I want to add a function similar to mlint-show-warning to my post-command-hook. It gets added to the hook in the "Hooks" section below.

(defun do.matlab/mlint-post-command ()
  "Show the warning for the current mark.
This is intended to be run after every command. It only prints a
message if there is a error at point."
  (let ((n (linemark-at-point (point) mlint-mark-group)))
    (when n
      (message (oref n warning)))))

Hooks

First, the hook for the MATLAB shell.

(defun do.matlab/shell-hooks ()
  (setq global-hl-line-mode nil)
  (setq-local ml-interactive? t) ;; for mode line
  (setq show-trailing-whitespace nil))
(add-hook 'matlab-shell-mode-hook 'do.matlab/shell-hooks)

Now the hook for matlab-mode. Mostly setting up mlint.

(defun do.matlab/hooks ()
  (flycheck-mode -1)
  (mlint-minor-mode 1)
  (highlight-symbol-mode)
  (add-hook 'post-command-hook 'do.matlab/mlint-post-command)
  (setq-local company-backends (remove 'company-capf do.completion/backend-list)))
(add-hook 'matlab-mode-hook 'do.matlab/hooks)