[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."
- [nongnu] elpa/rust-mode 367a89c 299/486: Allow formatting with long lines, (continued)
- [nongnu] elpa/rust-mode 367a89c 299/486: Allow formatting with long lines, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 610fe1f 305/486: Address review comments, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 72c479b 311/486: Add `rust-run-clippy' and `rust-buffer-project' with testing paraphernalia., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 4ec735e 300/486: Add stderr output from rustfmt on exit code 3, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 9afe997 313/486: Declare `rust-buffer-project' and require `json' at runtime., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode b4077f8 314/486: Add `rust-cargo-bin' custom variable., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode d2bb17c 315/486: Skip `rust-test-project-located' without cargo and avoid `find-file' in test., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 0985f5f 318/486: Merge pull request #217 from Aankhen/add-clippy-command, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 34cc528 319/486: Highlight question mark operator using new `rust-question-mark-face'., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 2a9fcd0 321/486: Add syntax highlighting and imenu support for `union`, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 89320ad 323/486: Highlight interpolation in arguments to print! &c.,
ELPA Syncer <=
- [nongnu] elpa/rust-mode e48a650 329/486: Merge pull request #225 from Aankhen/add-var-colours, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 6e9db46 331/486: Merge pull request #230 from tromey/fix-indentation-bug, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 0e832ec 340/486: Remove `float`, `int`, and `uint` as built-in types., ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 302b537 344/486: Revert #216 and Add Suggestions from #226, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode b8e4908 345/486: Merge pull request #243 from iwillspeak/rust-compile-command, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 6bcb82b 350/486: Merge pull request #249 from brotzeit/rust--format-call, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 705f3e4 352/486: Improved font-locking for print macros, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 2fe1f9d 355/486: add dyn to keywords, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 0d408d5 357/486: Merge pull request #253 from jjwest/master, ELPA Syncer, 2021/08/07
- [nongnu] elpa/rust-mode 42c98bb 362/486: Merge pull request #263 from nikomatsakis/tweak-compilation-regexp, ELPA Syncer, 2021/08/07