Subject: bug#73877: 30; rust-ts-mode: highlight the possible type suffix of number literals

From: Christophe TROESTLER <[email-masked]>
Date: Mon, 28 Oct 2024 21:56:52 +0000

Hi,

Here is an updated patch that (hopefully) addresses all of your remarks except the last one: when executing the tests interactively in the instance of Emacs one works with, it is good IMHO that global variables are not changed under our feet.  That being said, it is not a strong opinion and I can remove the reset if you wish.

Best,
C.

On 27 October 2024 at 20:07 +01, Randy Taylor <[email-masked]> wrote:
> […] This should be against the master branch.
>
> It will need a commit message (see the CONTRIBUTE file) and a NEWS entry.
>
> +(defcustom rust-ts-mode-highlight-number-literal-type nil
> I'm unsure about this name since it's a little misleading. The type
> is still highlighted if this is nil, but the same as the number.
> I don't really have a better suggestion though, and anything else
> would be longer and probably a little unruly. I was thinking about
> rust-ts-mode-highlight-number-literal-type-separately but...
>
> Maybe we drop the literal part?
>
> Does anyone else have any thoughts/suggestions?
>
> +  :version "30.1"
> Should be "31.1".
>
> For rust-ts-test-font-lock, I'm wondering if we should split that up to
> a new ert-deftest. If we have more customizations in the future we'd
> probably end up doing that anyway.
> Also, I'm not sure we need to bother resetting the
> rust-ts-mode-highlight-number-literal-type variable.

From a2aa35a72a11387db8f0784de1e832220c187966 Mon Sep 17 00:00:00 2001
From: Christophe Troestler <[email-masked]>
Date: Fri, 18 Oct 2024 23:50:06 +0200
Subject: [PATCH] Rust ts: fontify as type the possible suffix of number
 literals
Content-Type: text/plain; charset="utf-8"

* lisp/progmodes/rust-ts-mode.el (rust-ts-mode--fontify-number-literal):
perform the improved fontification of numbers.
* test/lisp/progmodes/rust-ts-mode-tests.el:
* test/lisp/progmodes/rust-ts-mode-resources/font-lock.rs:
* test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs:
Add tests for the new optional fontification of the possible type
suffix of numbers.
---
 etc/NEWS                                      |  7 ++++
 lisp/progmodes/rust-ts-mode.el                | 36 ++++++++++++++++++-
 .../font-lock-number.rs                       | 18 ++++++++++
 .../rust-ts-mode-resources/font-lock.rs       | 18 ++++++++++
 test/lisp/progmodes/rust-ts-mode-tests.el     | 16 +++++++--
 5 files changed, 92 insertions(+), 3 deletions(-)
 create mode 100644 test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs

diff --git a/etc/NEWS b/etc/NEWS
index d1c7303f976..4a987f11f4b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -630,6 +630,13 @@ cloning, or prompts for that, too.
 When the argument is non-nil, the function switches to a buffer visiting
 the directory into which the repository was cloned.
 
+** Rust-ts mode
+
+---
+*** New user option 'rust-ts-mode-fontify-number-suffix-as-type'.
+Rust number literals may have an optional type suffix.  When this option
+is non-nil, this suffix is fontified as a type.
+
 
 * New Modes and Packages in Emacs 31.1
 
diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index e52ea3b125a..4314fa9aeed 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -62,6 +62,15 @@ rust-ts-flymake-command
                  (repeat :tag "Custom command" string))
   :group 'rust)
 
+(defcustom rust-ts-mode-fontify-number-suffix-as-type nil
+  "If non-nil, fontify the optional type suffix of number literals with
+`font-lock-type-face' instead of `font-lock-number-face'."
+  :version "31.1"
+  :type 'boolean
+  :group 'rust)
+(put 'rust-ts-mode-fontify-number-suffix-as-type 'safe-local-variable
+     'booleanp)
+
 (defvar rust-ts-mode-prettify-symbols-alist
   '(("&&" . ?∧) ("||" . ?∨)
     ("<=" . ?≤)  (">=" . ?≥) ("!=" . ?≠)
@@ -116,6 +125,12 @@ rust-ts-mode--indent-rules
      ((parent-is "use_list") parent-bol rust-ts-mode-indent-offset)))
   "Tree-sitter indent rules for `rust-ts-mode'.")
 
+(defconst rust-ts-mode--number-types
+  (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64"
+                "i64" "u128" "i128" "usize" "isize" "f32" "f64"))
+  "Regexp that matches any suffix on number literals as documented
+at https://doc.rust-lang.org/reference/tokens.html#suffixes.")
+
 (defvar rust-ts-mode--builtin-macros
   '("concat_bytes" "concat_idents" "const_format_args"
     "format_args_nl" "log_syntax" "trace_macros" "assert" "assert_eq"
@@ -221,7 +236,8 @@ rust-ts-mode--font-lock-settings
 
    :language 'rust
    :feature 'number
-   '([(float_literal) (integer_literal)] @font-lock-number-face)
+   '([(float_literal) (integer_literal)]
+     @rust-ts-mode--fontify-number-literal)
 
    :language 'rust
    :feature 'operator
@@ -369,6 +385,24 @@ 'rust-ts-mode--fontify-pattern
              (treesit-node-start id) (treesit-node-end id)
              'font-lock-variable-name-face override start end)))))))
 
+(defun rust-ts-mode--fontify-number-literal (node override start stop &rest _)
+  "Fontify number literals, highlighting the optional suffix as a type
+based on the value of `rust-ts-mode-highlight-number-literal-type'."
+  (let* ((beg (treesit-node-start node))
+         (end (treesit-node-end node)))
+    (save-excursion
+      (goto-char end)
+      (if (and rust-ts-mode-fontify-number-suffix-as-type
+               (looking-back rust-ts-mode--number-types beg))
+          (let* ((ty (match-beginning 0))
+                 (nb (if (eq (char-before ty) ?_) (1- ty) ty)))
+            (treesit-fontify-with-override
+             ty end 'font-lock-type-face override start stop)
+            (treesit-fontify-with-override
+             beg nb 'font-lock-number-face override start stop))
+          (treesit-fontify-with-override
+           beg end 'font-lock-number-face override start stop)))))
+
 (defun rust-ts-mode--defun-name (node)
   "Return the defun name of NODE.
 Return nil if there is no name or if NODE is not a defun node."
diff --git a/test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs b/test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs
new file mode 100644
index 00000000000..1c9a38bd683
--- /dev/null
+++ b/test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs
@@ -0,0 +1,18 @@
+
+fn main() {
+    let x = 1usize;
+//          ^ font-lock-number-face
+//             ^ font-lock-type-face
+    let x = 1_usize;
+//          ^ font-lock-number-face
+//             ^ font-lock-type-face
+    let x = 1_f64;
+//          ^ font-lock-number-face
+//             ^ font-lock-type-face
+    let x = 1.0f64;
+//          ^ font-lock-number-face
+//              ^ font-lock-type-face
+    let x = 1.0_f64;
+//          ^ font-lock-number-face
+//              ^ font-lock-type-face
+}
diff --git a/test/lisp/progmodes/rust-ts-mode-resources/font-lock.rs b/test/lisp/progmodes/rust-ts-mode-resources/font-lock.rs
index 377cda0e3b9..1f549085e3f 100644
--- a/test/lisp/progmodes/rust-ts-mode-resources/font-lock.rs
+++ b/test/lisp/progmodes/rust-ts-mode-resources/font-lock.rs
@@ -23,3 +23,21 @@ macro_rules! unsafe_foo {
 //                                     ^ font-lock-operator-face
         }
     };
+
+fn main() {
+    let x = 1usize;
+//          ^ font-lock-number-face
+//             ^ font-lock-number-face
+    let x = 1_usize;
+//          ^ font-lock-number-face
+//             ^ font-lock-number-face
+    let x = 1_f64;
+//          ^ font-lock-number-face
+//             ^ font-lock-number-face
+    let x = 1.0f64;
+//          ^ font-lock-number-face
+//              ^ font-lock-number-face
+    let x = 1.0_f64;
+//            ^ font-lock-number-face
+//               ^ font-lock-number-face
+}
diff --git a/test/lisp/progmodes/rust-ts-mode-tests.el b/test/lisp/progmodes/rust-ts-mode-tests.el
index f718a57fc9e..fe82ce7ad7e 100644
--- a/test/lisp/progmodes/rust-ts-mode-tests.el
+++ b/test/lisp/progmodes/rust-ts-mode-tests.el
@@ -26,8 +26,20 @@
 
 (ert-deftest rust-ts-test-font-lock ()
   (skip-unless (treesit-ready-p 'rust))
-  (let ((treesit-font-lock-level 4))
-    (ert-font-lock-test-file (ert-resource-file "font-lock.rs") 'rust-ts-mode)))
+  (let ((treesit-font-lock-level 4)
+        (h rust-ts-mode-fontify-number-suffix-as-type))
+    (setq rust-ts-mode-fontify-number-suffix-as-type nil)
+    (ert-font-lock-test-file (ert-resource-file "font-lock.rs") 'rust-ts-mode)
+    (setq rust-ts-mode-fontify-number-suffix-as-type h)))
+
+(ert-deftest rust-ts-test-font-lock-number ()
+  (skip-unless (treesit-ready-p 'rust))
+  (let ((treesit-font-lock-level 4)
+        (h rust-ts-mode-fontify-number-suffix-as-type))
+    (setq rust-ts-mode-fontify-number-suffix-as-type t)
+    (ert-font-lock-test-file (ert-resource-file "font-lock-number.rs")
+                             'rust-ts-mode)
+    (setq rust-ts-mode-fontify-number-suffix-as-type h)))
 
 (provide 'rust-ts-mode-tests)
 
-- 
2.45.2