branch: externals/matlab-mode
commit 75f9123d36af8cc75a01992f4b25b43074472fca
Author: John Ciolfi <[email protected]>
Commit: John Ciolfi <[email protected]>

    matlab-ts-grammar-install: now does all install activities
---
 doc/install-matlab-tree-sitter-grammar.org |  65 +++++----
 matlab-ts-grammar-install.el               | 213 ++++++++++++++++++++++-------
 2 files changed, 200 insertions(+), 78 deletions(-)

diff --git a/doc/install-matlab-tree-sitter-grammar.org 
b/doc/install-matlab-tree-sitter-grammar.org
index ee3cbbac19..65c37feb2b 100644
--- a/doc/install-matlab-tree-sitter-grammar.org
+++ b/doc/install-matlab-tree-sitter-grammar.org
@@ -31,11 +31,29 @@
 
 #+title: How to install the MATLAB tree-sitter grammar
 #+author: John Ciolfi
-#+date: [2025-09-04]
+#+date: [2025-11-13]
 
 The MATLAB tree-sitter grammar requires Emacs 30 or later. This is typically 
installed in
 =~/.emacs.d/tree-sitter/libtree-sitter-matlab.so= (=.dll= on Windows, =.dylib= 
on Mac).
 
+* Install the Emacs-MATLAB-Mode pre-built matlab tree-sitter grammar
+
+After installing matlab-mode,
+
+ : M-x matlab-ts-grammar-install
+
+This will download the pre-build matlab tree-sitter grammar from
+https://github.com/mathworks/Emacs-MATLAB-Mode/matlab-ts-bin and save to the 
tree-sitter
+subdirectory of `user-emacs-directory'. For example,
+=~/.emacs.d/tree-sitter/libtree-sitter-matlab.so= (=.dylib= on Mac, =.dll= on 
Windows). You can
+visit https://github.com/mathworks/Emacs-MATLAB-Mode/matlab-ts-bin to see what 
source was used to
+produce these binaries.
+
+In addition, =M-x matlab-ts-grammar-install= will save the customizations 
required for
+=matlab-ts-mode= which leverages the matlab tree-sitter grammar.
+
+* Alternative: Manual Installation
+
 Below we use the location =~/emacs-projects=. You can change this to another 
location if desired.
 
 As described below, installation consists of two steps
@@ -43,27 +61,13 @@ As described below, installation consists of two steps
 1. Install the MATLAB tree-sitter grammar
 2. Setup Emacs to use the tree-sitter grammar
 
-* Install MATLAB tree-sitter grammar for Emacs 30
-
 For Emacs 30, you need to build the MATLAB tree-sitter grammar for ABI 14 
because Emacs 30 uses
 version 14 of the tree-sitter application binary interface and as of Sep-2025, 
the latest
 tree-sitter ABI is 15. There are different methods for installing this.
 
-** Method 1 - Install the Emacs-MATLAB-Mode pre-built matlab tree-sitter 
grammar
+** Install MATLAB tree-sitter grammar for Emacs 30
 
-After installing the =matlab-mode= package,
-
-: M-x matlab-ts-grammar-install
-
-This will download the pre-build matlab tree-sitter grammar from
-https://github.com/mathworks/Emacs-MATLAB-Mode/matlab-ts-bin and save to the
-tree-sitter subdirectory of `user-emacs-directory'. For example,
-=~/.emacs.d/tree-sitter/libtree-sitter-matlab.so= (=.dylib= on Mac, =.dll= on 
Windows).
-
-Visit https://github.com/mathworks/Emacs-MATLAB-Mode/matlab-ts-bin to see what 
source was
-used to produce these binaries.
-
-** Method 2 - Install from abi/14 branch
+*** Method 1 - Install from abi/14 branch
 
 Oct-29-2025 matlab-ts-mode is known to work with matlab tree-sitter v1.2.3 
branch 
[[https://github.com/acristoffers/tree-sitter-matlab/tree/abi/14][abi/14]] at 
[[https://github.com/acristoffers/tree-sitter-matlab/tree/9a4e65df4bb08e2b019ca2ef16b2d8f3d95ce978][9a4e65d]].
 
@@ -120,7 +124,7 @@ Oct-29-2025 matlab-ts-mode is known to work with matlab 
tree-sitter v1.2.3 branc
        cl /LD /I src\tree_sitter src\parser.c src\scanner.c /link 
/out:%HOME%\.emacs.d\tree-sitter\libtree-sitter-matlab.dll
      #+end_src
 
-** Method 3 - Generate the grammar for ABI 14 and install
+*** Method 2 - Generate the grammar for ABI 14 and install
 
 1. Install C/C++ compiler if not installed. See Method 2 for tips on 
installing the C/C++ compiler.
 
@@ -187,25 +191,30 @@ Oct-29-2025 matlab-ts-mode is known to work with matlab 
tree-sitter v1.2.3 branc
 
    - _Windows_
 
-     Follow the same Windows build step as in Method 2.
+     Follow the same Windows build step as in Method 1.
 
-* Setup Emacs to use the MATLAB tree-sitter grammar
+** Setup Emacs to use the MATLAB tree-sitter grammar
 
 Tell Emacs to use *matlab-ts-mode* for MATLAB files by adding the following to 
your
 =user-init-file= which is typically =~/.emacs=, or add it to your 
=site-run-file=
 
-#+begin_src emacs-lisp
-  (add-to-list 'major-mode-remap-alist '(matlab-mode . matlab-ts-mode))
-#+end_src
+1. Map =matlab-mode= to =matlab-ts-mode=
+
+   : M-x customize-variable RET major-mode-remap-alist RET
+
+   and INS (insert):
+
+   : Key: matlab-mode
+   :    Value: matlab-ts-mode
 
-Tell =org-mode= that =#+begin_src matlab ... #end_src= blocks should use 
*matlab-ts-mode*:
+2. Tell =org-mode= that =#+begin_src matlab ... #end_src= blocks should use 
*matlab-ts-mode*
 
- : M-x customize-variable RET org-src-lang-modes RET
+   : M-x customize-variable RET org-src-lang-modes RET
 
-and map matlab to matlab-ts:
+   and map matlab to matlab-ts:
 
- : Language name: matlab
- : Major mode: matlab-ts
+    : Language name: matlab
+    : Major mode: matlab-ts
 
 # LocalWords:  showall usepackage parskip tocloft cftsecnumwidth 
cftsubsecindent cftsubsecnumwidth
 # LocalWords:  libtree dylib workarea ABI langs abi MSys sudo treesit nodejs 
npm alist lang MELPA
diff --git a/matlab-ts-grammar-install.el b/matlab-ts-grammar-install.el
index d315dff295..04ba3c6edb 100644
--- a/matlab-ts-grammar-install.el
+++ b/matlab-ts-grammar-install.el
@@ -1,4 +1,4 @@
-;;; matlab-ts-langs-install.el --- -*- lexical-binding: t -*-
+;;; matlab-ts-grammar-install.el --- -*- lexical-binding: t -*-
 
 ;; Copyright (C) 2025 Free Software Foundation, Inc.
 ;;
@@ -18,50 +18,63 @@
 ;;; Commentary:
 ;;
 ;; Download
-;;   ~/.emacs.d/tree-sitter/libtree-sitter-LANGUAGES.so (or .dll or .dylib)
+;;   ~/.emacs.d/tree-sitter/libtree-sitter-matlab.so (or .dll or .dylib)
 ;; from
 ;;   https://github.com/mathworks/Emacs-MATLAB-Mode/matlab-ts-bin/
-;;
 
 ;;; Code:
 
+(require 'files)
+(require 'org-src)
 (require 'url)
 
 (defun matlab--ts-grammar-arch-and-shared-lib ()
   "Return ARCH/libtree-sitter-matlab.SLIB_EXT.
 ARCH is the same as the MATLAB computer('arch') command result."
   (let ((result (pcase system-type
-                 ('darwin (cond
+                  ('darwin (cond
                             ((string-prefix-p "aarch64" system-configuration)
                              "maca64/libtree-sitter-matlab.dylib")
                             ((string-prefix-p "x86_64" system-configuration)
                              "maca64/libtree-sitter-matlab.dylib")))
-                 ('gnu/linux (cond
+                  ('gnu/linux (cond
                                ((string-prefix-p "x86_64" system-configuration)
                                 "glnxa64/libtree-sitter-matlab.so")))
                   ('windows-nt "win64/libtree-sitter-matlab.dll"))))
     (when (not result)
-      (error "Unsupported system-type %s, system-configuration %s" system-type
-             system-configuration))
+      (user-error "Unsupported system-type %s, system-configuration %s" 
system-type
+                  system-configuration))
     result))
 
-(defun matlab--ts-grammar-download-url (branch)
-  "Get the download tree-sitter-matlab URL for BRANCH."
+(defun matlab--ts-grammar-download-url (branch prompt-for-version)
+  "Get the download tree-sitter-matlab URL for BRANCH.
+When PROMPT-FOR-VERSION is non-nil, prompt for the version to download."
 
   ;; Use GitHub REST API to get the download URL
   (let* ((bin-url (concat 
"https://api.github.com/repos/mathworks/Emacs-MATLAB-mode/contents/";
                           "matlab-ts-bin?ref=" branch))
          (raw-url-start (concat 
"https://raw.githubusercontent.com/mathworks/Emacs-MATLAB-Mode/";
                                 branch "/matlab-ts-bin"))
-          
-        (bin-buf (url-retrieve-synchronously bin-url))
+
+         (bin-buf (url-retrieve-synchronously bin-url))
          (versions '())
          latest-ver-date-num
          latest-ver
-        download-url)
+         download-url)
 
     (with-current-buffer bin-buf
-      (let ((entries (json-read-from-string (cadr (split-string 
(buffer-string) "\n\n" t)))))
+      (let* ((response-content
+              (let ((result (buffer-string)))
+                (goto-char (point-min))
+                (if (re-search-forward "^\\(HTTP/[0-9.]* \\([0-9]+\\) .*\\)$" 
nil t)
+                    (let ((response (match-string 1))
+                          (response-code (match-string 2)))
+                      (when (not (string= response-code "200"))
+                        (user-error "Invalid response \"%s\" received from %s"
+                                    response bin-url)))
+                  (user-error "Unexpected result from %s: %s" bin-url result))
+                (cadr (split-string result "\n\n" t))))
+             (entries (json-read-from-string response-content)))
         (cl-loop for entry across entries do
                  (let ((rel-file (alist-get 'path entry))) ;; 
matlab-ts-bin/FILE
                    ;; Have YYYYMMDD-SHA1
@@ -75,59 +88,159 @@ ARCH is the same as the MATLAB computer('arch') command 
result."
                        (push ver versions)))))))
 
     (when (not latest-ver)
-      (error "Failed to get release versions from %s" bin-url))
+      (user-error "Failed to get release versions from %s" bin-url))
 
-    (let ((ver-to-download (completing-read (concat
-                                             "Version to download (" 
latest-ver " is latest): ")
-                                            versions nil t latest-ver))
-         (arch-slib (matlab--ts-grammar-arch-and-shared-lib)))
+    (let ((ver-to-download (if prompt-for-version
+                               (completing-read (concat
+                                                 "Version to download (" 
latest-ver " is latest): ")
+                                                versions nil t latest-ver)
+                             latest-ver))
+          (arch-slib (matlab--ts-grammar-arch-and-shared-lib)))
 
       (setq download-url (concat raw-url-start "/" ver-to-download "/" 
arch-slib)))
-      
+
     (kill-buffer bin-buf)
     download-url))
 
+(defun matlab--files-equal-p (file1 file2)
+  "Return t if the contents of FILE1 and FILE2 are identical, nil otherwise."
+  (let ((content1 (with-temp-buffer
+                    (insert-file-contents-literally file1)
+                    (buffer-string)))
+        (content2 (with-temp-buffer
+                    (insert-file-contents-literally file2)
+                    (buffer-string))))
+    (equal content1 content2)))
+
+(defun matlab--ts-grammar-setup ()
+  "Tell Emacs to use the matlab tree-sitter grammar shared library."
+
+  ;; 1. Add '(matlab-mode . matlab-ts-mode) to `major-mode-remap-alist' if 
needed.
+  (let* ((remap-alist major-mode-remap-alist)
+         (value (alist-get 'matlab-mode remap-alist)))
+
+    (when (and value
+               (not (eq value 'matlab-ts-mode))) ;; bad value?
+      (setq remap-alist (delq (assoc 'matlab-mode remap-alist) remap-alist))
+      (setq value nil))
+
+    (when (not value)
+      (push '(matlab-mode . matlab-ts-mode) remap-alist)
+      (message "Updating 'major-mode-remap-alist to contain '(matlab-mode . 
matlab-ts-mode)")
+      (customize-save-variable 'major-mode-remap-alist remap-alist)))
+
+  ;; 2. Add ("matlab" . "matlab-ts") to org-src-lang-modes
+  (let* ((lang-modes org-src-lang-modes)
+         (value (alist-get "matlab" lang-modes nil nil 'equal)))
+
+    (when (and value
+               (not (string= value "matlab-ts-mode"))) ;; bad value?
+      (setq lang-modes (delq (assoc "matlab" lang-modes) lang-modes))
+      (setq value nil))
+
+    (when (not value)
+      (push '("matlab" . "matlab-ts") lang-modes)
+      (message "Updating 'major-mode-remap-alist to contain '(\"matlab\" . 
\"matlab-ts\")")
+      (customize-save-variable 'org-src-lang-modes lang-modes))))
+
 ;;;###autoload
-(defun matlab-ts-grammar-install (&optional dir branch)
-  "Download the latest matlab tree-sitter grammar build to DIR.
+(defun matlab-ts-grammar-install (arg)
+  "Download the matlab tree-sitter grammar shared library.
 
-Optional BRANCH defaults to \"default\".
+Download libtree-sitter-matlab.SLIB-EXT (SLIB-EXT = so on Linux, dll on
+Windows, or dylib on Mac) from the matlab-ts-bin directory in
+https://github.com/mathworks/Emacs-MATLAB-Mode
 
-This will create
-   DIR/libtree-sitter-matlab.SLIB-EXT
-shared libraries where SLIB-EXT = so on Linux, dll on Windows, or dylib on Mac.
+The matlab tree-sitter grammar shared library is required for
+`matlab-ts-mode'.
 
-DIR defaults to tree-sitter subdirectory of `user-emacs-directory' which is
-typically ~/.emacs.d/tree-sitter/.
+With prefix ARG, prompt for
 
-The matlab tree-sitter grammar is required for `matlab-ts-mode'."
+ - DIR to place libtree-sitter-matlab.SLIB.EXT.  This defaults to the
+   tree-sitter subdirectory of `user-emacs-directory' which is typically
+   ~/.emacs.d/tree-sitter/.
 
-  (interactive)
+ - BRANCH in https://github.com/mathworks/Emacs-MATLAB-Mode.  This defaults
+   to the \"default\" branch.
 
-  (when (< emacs-major-version 30)
-    (error "Unsupported Emacs version, %d" emacs-major-version))
-
-  (when (not branch)
-    (setq branch "default"))
-  
-  (if (not dir)
-      (progn
-        (setq dir (concat user-emacs-directory "tree-sitter"))
-        (when (not (file-directory-p dir))
-          (make-directory dir t)))
-    ;; Else dir was provided and it must exist
-    (when (not (file-directory-p dir))
-      (error "%s is not an existing directory" dir)))
+ - VERSION of the shared library to download.  This defaults to the latest
+   version.
 
-  (setq dir (file-name-as-directory (file-truename dir)))
+When libtree-sitter-matlab.SLIB-EXT already exists on your system,
 
-  (let* ((download-url (matlab--ts-grammar-download-url branch))
-         (grammar-slib (concat dir (file-name-nondirectory download-url))))
-    (when (y-or-n-p (format "Download %s\nto %s/? " download-url grammar-slib))
-      (url-copy-file download-url grammar-slib t)
-      (message "Downloaded %s" grammar-slib))))
+ - If it is up-to-date, it will not be touched and a message is displayed
+   that it is up-to-date.
+
+ - If it is out-of-date, it will be updated and you will be prompted
+   to restart Emacs so the new library can be used."
+
+  (interactive "P")
+
+  (when (< emacs-major-version 30)
+    (user-error "Unsupported Emacs version, %d" emacs-major-version))
+
+  (let* ((branch-default "default")
+         (branch (if arg
+                     (let ((ans ""))
+                       (while (string= ans "")
+                         (setq ans (string-trim (read-string "Branch: " 
branch-default))))
+                       ans)
+                   branch-default))
+         (dir-default (concat user-emacs-directory "tree-sitter/"))
+         (dir (if arg
+                  (let ((ans ""))
+                    (while (string= ans "")
+                      (setq ans (read-directory-name "Download directory: " 
dir-default)))
+                    ans)
+                dir-default)))
+
+    (if (string= dir dir-default)
+        (when (not (file-directory-p dir))
+          (make-directory dir t))
+      (when (not (file-directory-p dir))
+        (user-error "%s is not an existing directory" dir)))
+
+    (setq dir (file-name-as-directory (file-truename dir)))
+
+    (let* ((download-url (matlab--ts-grammar-download-url branch arg))
+           (grammar-slib-base (file-name-nondirectory download-url))
+           (grammar-slib (concat dir grammar-slib-base))
+           (grammar-slib-tmp (concat grammar-slib ".tmp")))
+
+      (when (y-or-n-p (format "Download %s\nto %s? " download-url 
grammar-slib))
+
+        (url-copy-file download-url grammar-slib-tmp t)
+
+        (let (update-type)
+          (if (not (file-exists-p grammar-slib))
+              (setq update-type 'downloaded)
+            (if (matlab--files-equal-p grammar-slib grammar-slib-tmp)
+                (message "%s is already up-to-date and thus was not updated" 
grammar-slib)
+              (setq update-type 'downloaded-and-updated)))
+
+          (if (not update-type)
+              (delete-file grammar-slib-tmp)
+            ;; Update grammar-slib
+            (rename-file grammar-slib-tmp grammar-slib t)
+
+            ;; Tell Emacs to use matlab-ts-mode for *.m files and matlab 
org-mode source blocks
+            (matlab--ts-grammar-setup)
+
+            (pcase update-type
+              ('downloaded
+               (message "Downloaded %s" grammar-slib))
+              ('downloaded-and-updated
+               (let ((prompt (concat "Downloaded and updated " grammar-slib 
"\n"
+                                     "If the older " grammar-slib-base
+                                     " was in use, Emacs must be restarted.\n"
+                                     "Exit Emacs? ")))
+                 (when (y-or-n-p prompt)
+                   (save-buffers-kill-terminal))))
+              (_
+               (error "Assert - invalid update-type %S" update-type)))))))))
 
 (provide 'matlab-ts-grammar-install)
 ;;; matlab-ts-grammar-install.el ends here
 
-
+;; LocalWords:  libtree dylib defun SLIB pcase darwin aarch maca linux nt buf 
cadr alist YYYYMMDD
+;; LocalWords:  SHA setq slib truename nondirectory tmp delq lang repeat:nil

Reply via email to