emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/rust-mode 89320ad 323/486: Highlight interpolation in argu


From: ELPA Syncer
Subject: [nongnu] elpa/rust-mode 89320ad 323/486: Highlight interpolation in arguments to print! &c.
Date: Sat, 7 Aug 2021 09:25:45 -0400 (EDT)

branch: elpa/rust-mode
commit 89320ad26b8768e5db40e97c35be9f2b690ab9c5
Author: Aankhen <Aankhen@users.noreply.github.com>
Commit: Aankhen <Aankhen@users.noreply.github.com>

    Highlight interpolation in arguments to print! &c.
---
 rust-mode-tests.el | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 rust-mode.el       |  82 ++++++++++++++++++++
 2 files changed, 302 insertions(+)

diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 614340c..759f919 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -2024,6 +2024,226 @@ fn main() {
    '("r#\"\"\"#" font-lock-string-face
      "'q'" font-lock-string-face)))
 
+(ert-deftest rust-macro-font-lock ()
+  (rust-test-font-lock
+   "foo!\(\);"
+   '("foo!" font-lock-preprocessor-face))
+  (rust-test-font-lock
+   "foo!{};"
+   '("foo!" font-lock-preprocessor-face))
+  (rust-test-font-lock
+   "foo![];"
+   '("foo!" font-lock-preprocessor-face)))
+
+(ert-deftest rust-string-interpolation-matcher-works ()
+  (dolist (test '(("print!\(\"\"\)" 9 11 nil)
+                  ("print!\(\"abcd\"\)" 9 15 nil)
+                  ("print!\(\"abcd {{}}\"\);" 9 19 nil)
+                  ("print!\(\"abcd {{\"\);" 9 18 nil)
+                  ("print!\(\"abcd {}\"\);" 9 18 ((14 16)))
+                  ("print!\(\"abcd {{{}\"\);" 9 20 ((16 18)))
+                  ("print!\(\"abcd {}{{\"\);" 9 20 ((14 16)))
+                  ("print!\(\"abcd {} {{\"\);" 9 21 ((14 16)))
+                  ("print!\(\"abcd {}}}\"\);" 9 20 ((14 16)))
+                  ("print!\(\"abcd {{{}}}\"\);" 9 20 ((16 18)))
+                  ("print!\(\"abcd {0}\"\);" 9 18 ((14 17)))
+                  ("print!\(\"abcd {0} efgh\"\);" 9 23 ((14 17)))
+                  ("print!\(\"{1} abcd {0} efgh\"\);" 9 27 ((9 12) (18 21)))
+                  ("print!\(\"{{{1} abcd }} {0}}} {{efgh}}\"\);" 9 33 ((11 14) 
(23 26)))))
+    (destructuring-bind (text cursor limit matches) test
+      (with-temp-buffer
+        ;; make sure we have a clean slate
+        (save-match-data
+          (set-match-data nil)
+          (insert text)
+          (goto-char cursor)
+          (if (null matches)
+              (should (equal (progn
+                               (rust-string-interpolation-matcher limit)
+                               (match-data))
+                             nil))
+            (dolist (pair matches)
+              (rust-string-interpolation-matcher limit)
+              (should (equal (match-beginning 0) (car pair)))
+              (should (equal (match-end 0) (cadr pair))))))))))
+
+(ert-deftest rust-formatting-macro-font-lock ()
+  ;; test that the block delimiters aren't highlighted and the comment
+  ;; is ignored
+  (rust-test-font-lock
+   "print!(\"\"); { /* print!(\"\"); */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "print!(\"\"); */" font-lock-comment-face))
+  ;; other delimiters
+  (rust-test-font-lock
+   "print!{\"\"}; { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; other delimiters
+  (rust-test-font-lock
+   "print![\"\"]; { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; no interpolation
+  (rust-test-font-lock
+   "print!(\"abcd\"); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"abcd\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; only interpolation
+  (rust-test-font-lock
+   "print!(\"{}\"); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"" font-lock-string-face
+     "{}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; text + interpolation
+  (rust-test-font-lock
+   "print!(\"abcd {}\", foo); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; text + interpolation with specification
+  (rust-test-font-lock
+   "print!(\"abcd {0}\", foo); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; text + interpolation with specification and escape
+  (rust-test-font-lock
+   "print!(\"abcd {0}}}\", foo); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     "}}\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; multiple pairs
+  (rust-test-font-lock
+   "print!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("print!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; println
+  (rust-test-font-lock
+   "println!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("println!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; eprint
+  (rust-test-font-lock
+   "eprint!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("eprint!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; eprintln
+  (rust-test-font-lock
+   "eprintln!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("eprintln!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; format
+  (rust-test-font-lock
+   "format!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("format!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; print + raw string
+  (rust-test-font-lock
+   "format!(r\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("format!" rust-builtin-formatting-macro-face
+     "r\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; print + raw string with hash
+  (rust-test-font-lock
+   "format!(r#\"abcd {0} efgh {1}\"#, foo, bar); { /* no-op */ }"
+   '("format!" rust-builtin-formatting-macro-face
+     "r#\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"#" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  ;; print + raw string with two hashes
+  (rust-test-font-lock
+   "format!(r##\"abcd {0} efgh {1}\"##, foo, bar); { /* no-op */ }"
+   '("format!" rust-builtin-formatting-macro-face
+     "r##\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     " efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"##" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face)))
+
+(ert-deftest rust-write-macro-font-lock ()
+  (rust-test-font-lock
+   "write!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("write!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     "}} efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face))
+  (rust-test-font-lock
+   "writeln!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }"
+   '("writeln!" rust-builtin-formatting-macro-face
+     "\"abcd " font-lock-string-face
+     "{0}" rust-string-interpolation-face
+     "}} efgh " font-lock-string-face
+     "{1}" rust-string-interpolation-face
+     "\"" font-lock-string-face
+     "/* " font-lock-comment-delimiter-face
+     "no-op */" font-lock-comment-face)))
+
 (ert-deftest rust-test-basic-paren-matching ()
   (rust-test-matching-parens
    "
diff --git a/rust-mode.el b/rust-mode.el
index eac246f..8ae2454 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -170,7 +170,18 @@ function or trait.  When nil, where will be aligned with 
fn or trait."
   "Face for the question mark operator."
   :group 'rust-mode)
 
+(defface rust-builtin-formatting-macro-face
+  '((t :inherit font-lock-builtin-face))
+  "Face for builtin formatting macros (print! &c.)."
+  :group 'rust-mode)
+
+(defface rust-string-interpolation-face
+  '((t :slant italic :inherit font-lock-string-face))
+  "Face for interpolating braces in builtin formatting macro strings."
+  :group 'rust-mode)
+
 (defun rust-paren-level () (nth 0 (syntax-ppss)))
+(defun rust-in-str () (nth 3 (syntax-ppss)))
 (defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
 (defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
 
@@ -573,6 +584,54 @@ the desired identifiers), but does not match type 
annotations \"foo::<\"."
             ((not (looking-at (rx (0+ space) "<")))
             (throw 'rust-path-font-lock-matcher match))))))))
 
+(defun rust-next-string-interpolation (limit)
+  "Search forward from point for next Rust interpolation marker
+before LIMIT.
+Set point to the end of the occurrence found, and return match beginning
+and end."
+  (catch 'match
+    (save-match-data
+      (save-excursion
+        (while (search-forward "{" limit t)
+          (if (eql (char-after (point)) ?{)
+              (forward-char)
+            (let ((start (match-beginning 0)))
+              ;; According to fmt_macros::Parser::next, an opening brace
+              ;; must be followed by an optional argument and/or format
+              ;; specifier, then a closing brace. A single closing brace
+              ;; without a corresponding unescaped opening brace is an
+              ;; error. We don't need to do anything special with
+              ;; arguments, specifiers, or errors, so we only search for
+              ;; the single closing brace.
+              (when (search-forward "}" limit t)
+                (throw 'match (list start (point)))))))))))
+
+(defun rust-string-interpolation-matcher (limit)
+  "Match next Rust interpolation marker before LIMIT and set
+match data if found. Returns nil if not within a Rust string."
+  (when (rust-in-str)
+    (let ((match (rust-next-string-interpolation limit)))
+      (when match
+        (set-match-data match)
+        (goto-char (cadr match))
+        match))))
+
+(defvar rust-builtin-formatting-macros
+  '("eprint"
+    "eprintln"
+    "format"
+    "print"
+    "println")
+  "List of builtin Rust macros for string formatting used by 
`rust-mode-font-lock-keywords'. (`write!' is handled separately.)")
+
+(defvar rust-formatting-macro-opening-re
+  "[[:space:]]*[({[][[:space:]]*"
+  "Regular expression to match the opening delimiter of a Rust formatting 
macro.")
+
+(defvar rust-start-of-string-re
+  "\\(?:r#*\\)?\""
+  "Regular expression to match the start of a Rust raw string.")
+
 (defvar rust-mode-font-lock-keywords
   (append
    `(
@@ -590,6 +649,22 @@ the desired identifiers), but does not match type 
annotations \"foo::<\"."
      (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]"))
       1 font-lock-preprocessor-face keep)
 
+     ;; Builtin formatting macros
+     (,(concat (rust-re-grab (concat (regexp-opt 
rust-builtin-formatting-macros) "!")) (concat rust-formatting-macro-opening-re 
rust-start-of-string-re))
+      (1 'rust-builtin-formatting-macro-face)
+      (rust-string-interpolation-matcher
+       (rust-end-of-string)
+       nil
+       (0 'rust-string-interpolation-face t nil)))
+
+     ;; write! macro
+     (,(concat (rust-re-grab "write\\(ln\\)?!") (concat 
rust-formatting-macro-opening-re "[[:space:]]*[^\"]+,[[:space:]]*" 
rust-start-of-string-re))
+      (1 'rust-builtin-formatting-macro-face)
+      (rust-string-interpolation-matcher
+       (rust-end-of-string)
+       nil
+       (0 'rust-string-interpolation-face t nil)))
+
      ;; Syntax extension invocations like `foo!`, highlight including the !
      (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]")
       1 font-lock-preprocessor-face)
@@ -1215,6 +1290,13 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
     ;; There is no opening brace, so consider the whole buffer to be one 
"defun"
     (goto-char (point-max))))
 
+(defun rust-end-of-string ()
+  "Skip to the end of the current string."
+  (save-excursion
+    (skip-syntax-forward "^\"|")
+    (skip-syntax-forward "\"|")
+    (point)))
+
 ;; Formatting using rustfmt
 (defun rust--format-call (buf)
   "Format BUF using rustfmt."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]