Subject: 26.2; nxml-mode doesn't fontify attributes in single quotes

From: Noam Postavsky <[email-masked]>
Date: Mon, 22 Apr 2019 12:08:55 -0400

X-Debbugs-CC: Stefan Monnier <[email-masked]>
Tags: patch

In an nxml-mode buffer, put the following text:

<x a="foo" b='bar'/>

Only "foo" is fontified in as a string, while bar is not.  This is a
regression since Emacs 25.3.  The patch below seems solves problem,
though I'm not entirely sure about it: there are some confusing comments
in sgml-mode.el about drawbacks and tradeoffs of recognizing single
quotes that I'm not really following (are they applicable to SGML only,
and not XML?).

There's also the difference that attribute values are now fontified with
font-lock-string-face rather than nxml-attribute-value, though I'm not
sure that's worth fixing.

>From a3d2dbcd0c4e272802a45a7c751877b6982fb257 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Sun, 21 Apr 2019 22:44:50 -0400
Subject: [PATCH] Make nxml-mode recognize single quote attribute again

* lisp/nxml/nxml-mode.el (nxml-syntax-propertize): New function.
(nxml-tag-syntax-table): New constant.
(nxml-mode): Set them as syntax-propertize-function and
syntax-ppss-table, respectively.
* test/lisp/nxml/nxml-mode-tests.el (nxml-mode-font-lock-quotes): New
test.
---
 lisp/nxml/nxml-mode.el            | 22 ++++++++++++++++++++--
 test/lisp/nxml/nxml-mode-tests.el | 20 ++++++++++++++++++++
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/lisp/nxml/nxml-mode.el b/lisp/nxml/nxml-mode.el
index ab035b927e..94fbe4b781 100644
--- a/lisp/nxml/nxml-mode.el
+++ b/lisp/nxml/nxml-mode.el
@@ -426,6 +426,24 @@ (defun nxml-parent-document-set (parent-document)
 (defvar tildify-space-string)
 (defvar tildify-foreach-region-function)
 
+(defun nxml-syntax-propertize (start end)
+  "Syntactic keywords for `nxml-mode'."
+  (goto-char start)
+  (sgml-syntax-propertize-inside end)
+  (funcall
+   (syntax-propertize-rules
+    sgml-syntax-propertize-rules
+    ;; Like the " rule in `sgml-syntax-propertize-rules', but for '.
+    ("'" (0 (if (prog1 (zerop (car (syntax-ppss (match-beginning 0))))
+                  (goto-char (match-end 0)))
+                (string-to-syntax ".")))))
+   start end))
+
+(defconst nxml-tag-syntax-table
+  (let ((table (make-syntax-table sgml-tag-syntax-table)))
+    (modify-syntax-entry ?\' "\"'" table))
+  "Syntax table used to parse XML tags.")
+
 ;;;###autoload
 (define-derived-mode nxml-mode text-mode "nXML"
   ;; We use C-c C-i instead of \\[nxml-balanced-close-start-tag-inline]
@@ -517,8 +535,8 @@ (define-derived-mode nxml-mode text-mode "nXML"
       (with-silent-modifications
 	(nxml-with-invisible-motion
 	  (nxml-scan-prolog)))))
-  (setq-local syntax-ppss-table sgml-tag-syntax-table)
-  (setq-local syntax-propertize-function #'sgml-syntax-propertize)
+  (setq-local syntax-ppss-table nxml-tag-syntax-table)
+  (setq-local syntax-propertize-function #'nxml-syntax-propertize)
   (add-hook 'change-major-mode-hook #'nxml-cleanup nil t)
 
   ;; Emacs 23 handles the encoding attribute on the xml declaration
diff --git a/test/lisp/nxml/nxml-mode-tests.el b/test/lisp/nxml/nxml-mode-tests.el
index 57a731ad18..92744be619 100644
--- a/test/lisp/nxml/nxml-mode-tests.el
+++ b/test/lisp/nxml/nxml-mode-tests.el
@@ -58,5 +58,25 @@ (ert-deftest nxml-balanced-close-start-tag-inline ()
     (nxml-balanced-close-start-tag-inline)
     (should (equal (buffer-string) "<a><b c=\"\"></b></a>"))))
 
+(ert-deftest nxml-mode-font-lock-quotes ()
+  (with-temp-buffer
+    (nxml-mode)
+    (insert "<x a=\"dquote attr\" b='squote attr'>\"dquote text\"'squote text'</x>")
+    (font-lock-ensure)
+    (let ((squote-txt-pos (search-backward "squote text"))
+          (dquote-txt-pos (search-backward "dquote text"))
+          (squote-att-pos (search-backward "squote attr"))
+          (dquote-att-pos (search-backward "dquote attr")))
+      ;; Just make sure that each quote uses the same face for quoted
+      ;; attribute values, and a different face for quoted text
+      ;; outside tags.  Don't test `font-lock-string-face' vs
+      ;; `nxml-attribute-value' here.
+      (should (equal (get-text-property squote-att-pos 'face)
+                     (get-text-property dquote-att-pos 'face)))
+      (should (equal (get-text-property squote-txt-pos 'face)
+                     (get-text-property dquote-txt-pos 'face)))
+      (should-not (equal (get-text-property squote-txt-pos 'face)
+                         (get-text-property dquote-att-pos 'face))))))
+
 (provide 'nxml-mode-tests)
 ;;; nxml-mode-tests.el ends here
-- 
2.11.0