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

From: Christophe TROESTLER <[email-masked]>
Date: Mon, 18 Nov 2024 15:14:40 +0000

On 15 November 2024 at 01:11 +01, Randy Taylor <[email-masked]> wrote:
> […] the patch didn't apply cleanly for me […]

Here is an updated version of the patch against today's master branch.

> […] If you do the same as treesit-font-lock-level, […]

For some reason it did not work in the past but it does now, so that's how I proceeded.

> You will also need to: (require 'rust-ts-mode)

Ok — some tests do that, others don't, so I did not originally change that.  I've now added the “require”.

> After you make that change, I think you should be able to get rid of
> this:
> +(put 'rust-ts-mode-fontify-number-suffix-as-type 'safe-local-variable
> +     'booleanp)

I first thought that one may want to set that change for specific file but, you are right, it is more of a user choice that should be set in your own config. files.

> In the font-lock-number.rs file, it looks like there is an extraneous
> newline at the top of the file.

Fixed.

> BTW the current patch still doesn't apply against the latest master
> for me.

I have rebased it on the current origin/master.

Best,
C.

From 616a0fc2a563b0c5b4fd74361540004560364305 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.  (Bug#73877)
* 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                | 37 ++++++++++++++++++-
 .../font-lock-number.rs                       | 17 +++++++++
 .../rust-ts-mode-resources/font-lock.rs       | 18 +++++++++
 test/lisp/progmodes/rust-ts-mode-tests.el     | 14 ++++++-
 5 files changed, 90 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 b6c21018779..1696c17892d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -719,6 +719,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 using the 'font-lock-type-face'.
+
 
 * 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..7a421eb506b 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, suffixes of number literals are fontified as types.
+In Rust, number literals can possess an optional type suffix.  When this
+variable is non-nil, these suffixes are fontified using
+`font-lock-type-face' instead of `font-lock-number-face'."
+  :version "31.1"
+  :type 'boolean
+  :group 'rust)
+
 (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 matching type suffixes of number literals.
+See 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,25 @@ '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 type suffix.
+If `rust-ts-mode-fontify-number-suffix-as-type' is non-nil, use
+`font-lock-type-face' to highlight the suffix."
+  (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..ed5b222d5ff
--- /dev/null
+++ b/test/lisp/progmodes/rust-ts-mode-resources/font-lock-number.rs
@@ -0,0 +1,17 @@
+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..3a710b3af3b 100644
--- a/test/lisp/progmodes/rust-ts-mode-tests.el
+++ b/test/lisp/progmodes/rust-ts-mode-tests.el
@@ -23,11 +23,21 @@
 (require 'ert-font-lock)
 (require 'ert-x)
 (require 'treesit)
+(require 'rust-ts-mode)
 
 (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)
+        (rust-ts-mode-fontify-number-suffix-as-type nil))
+    (ert-font-lock-test-file (ert-resource-file "font-lock.rs")
+                             'rust-ts-mode)))
+
+(ert-deftest rust-ts-test-font-lock-number ()
+  (skip-unless (treesit-ready-p 'rust))
+  (let ((treesit-font-lock-level 4)
+        (rust-ts-mode-fontify-number-suffix-as-type t))
+    (ert-font-lock-test-file (ert-resource-file "font-lock-number.rs")
+                             'rust-ts-mode)))
 
 (provide 'rust-ts-mode-tests)
 
-- 
2.45.2