aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Nazarewicz2018-03-31 14:16:54 +0100
committerMichal Nazarewicz2018-04-07 11:16:12 +0100
commit8d3bb7beb4bfab60ba31505728f8f945116d7a40 (patch)
treec8bea63e9079c07dd69193ac3cf6ddaf6af1d7d3
parent358da4565b589570759ddc9c2d1043405fdbb26e (diff)
downloademacs-8d3bb7beb4bfab60ba31505728f8f945116d7a40.tar.gz
emacs-8d3bb7beb4bfab60ba31505728f8f945116d7a40.zip
Handle quotation marks and apostrophes in ‘sgml-quote’
To be able to use text in an HTML argument, quotation marks need to be replaced with an appropriate character reference. Make ‘sgml-quote’ do that. While at it, fix entiteis not being unquoted if they lack closing semicolon (e.g. ‘&amp’) occuring at the very end of a region. Even though unlikely, make ‘sgml-quote’ handle this scenario. * lisp/textmodes/sgml-mode.el (sgml-quote): Handle quotation marks and apostrophes. Match entities lacking semicolon at the end of regions. * test/lisp/textmodes/sgml-mode-tests.el (sgml-quote-works): New test case for ‘sgml-quote’ function.
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/textmodes/sgml-mode.el26
-rw-r--r--test/lisp/textmodes/sgml-mode-tests.el30
3 files changed, 56 insertions, 7 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 02b31ecff47..ac60a365841 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -208,6 +208,13 @@ Can be controlled via the new variable 'footnote-align-to-fn-text'.
208formats (e.g. "black" => "#000000" => "rgb(0, 0, 0)") has been added, 208formats (e.g. "black" => "#000000" => "rgb(0, 0, 0)") has been added,
209bound to 'C-c C-f'. 209bound to 'C-c C-f'.
210 210
211** SGML mode
212
213---
214*** 'sgml-quote' now handles double quotes and apostrophes
215when escaping text and in addition all numeric entities when
216unescaping text.
217
211** Dired 218** Dired
212 219
213+++ 220+++
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index f6bdfc63844..52d14bd800c 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -1241,8 +1241,11 @@ See `sgml-tag-alist' for info about attribute rules."
1241 1241
1242(defun sgml-quote (start end &optional unquotep) 1242(defun sgml-quote (start end &optional unquotep)
1243 "Quote SGML text in region START ... END. 1243 "Quote SGML text in region START ... END.
1244Only &, < and > are quoted, the rest is left untouched. 1244Only &, <, >, ' and \" characters are quoted, the rest is left
1245With prefix argument UNQUOTEP, unquote the region." 1245untouched. This is sufficient to use quoted text as SGML argument.
1246
1247With prefix argument UNQUOTEP, unquote the region. All numeric entities,
1248\"amp\", \"lt\", \"gt\" and \"quot\" named entities are unquoted."
1246 (interactive "r\nP") 1249 (interactive "r\nP")
1247 (save-restriction 1250 (save-restriction
1248 (narrow-to-region start end) 1251 (narrow-to-region start end)
@@ -1250,14 +1253,23 @@ With prefix argument UNQUOTEP, unquote the region."
1250 (if unquotep 1253 (if unquotep
1251 ;; FIXME: We should unquote other named character references as well. 1254 ;; FIXME: We should unquote other named character references as well.
1252 (while (re-search-forward 1255 (while (re-search-forward
1253 "\\(&\\(amp\\|\\(l\\|\\(g\\)\\)t\\)\\)[][<>&;\n\t \"%!'(),/=?]" 1256 "\\(&\\(amp\\|quot\\|lt\\|gt\\|#\\([0-9]+\\|[xX][0-9a-fA-F]+\\)\\)\\)\\([][<>&;\n\t \"%!'(),/=?]\\|$\\)"
1254 nil t) 1257 nil t)
1255 (replace-match (if (match-end 4) ">" (if (match-end 3) "<" "&")) t t 1258 (replace-match
1256 nil (if (eq (char-before (match-end 0)) ?\;) 0 1))) 1259 (string
1257 (while (re-search-forward "[&<>]" nil t) 1260 (or (cdr (assq (char-after (match-beginning 2))
1261 '((?a . ?&) (?q . ?\") (?l . ?<) (?g . ?>))))
1262 (let ((num (match-string 3)))
1263 (if (or (eq ?x (aref num 0)) (eq ?X (aref num 0)))
1264 (string-to-number (substring num 1) 16)
1265 (string-to-number num 10)))))
1266 t t nil (if (eq (char-before (match-end 0)) ?\;) 0 1)))
1267 (while (re-search-forward "[&<>\"']" nil t)
1258 (replace-match (cdr (assq (char-before) '((?& . "&amp;") 1268 (replace-match (cdr (assq (char-before) '((?& . "&amp;")
1259 (?< . "&lt;") 1269 (?< . "&lt;")
1260 (?> . "&gt;")))) 1270 (?> . "&gt;")
1271 (?\" . "&#34;")
1272 (?' . "&#39;"))))
1261 t t))))) 1273 t t)))))
1262 1274
1263(defun sgml-pretty-print (beg end) 1275(defun sgml-pretty-print (beg end)
diff --git a/test/lisp/textmodes/sgml-mode-tests.el b/test/lisp/textmodes/sgml-mode-tests.el
index 7ca6e676c64..6c0070ccb1e 100644
--- a/test/lisp/textmodes/sgml-mode-tests.el
+++ b/test/lisp/textmodes/sgml-mode-tests.el
@@ -131,5 +131,35 @@ The point is set to the beginning of the buffer."
131 (sgml-delete-tag 1) 131 (sgml-delete-tag 1)
132 (should (string= "Winter is comin'" (buffer-string))))) 132 (should (string= "Winter is comin'" (buffer-string)))))
133 133
134(ert-deftest sgml-quote-works ()
135 (let ((text "Foo<Bar> \"Baz\" 'Qux'\n"))
136 (with-temp-buffer
137 ;; Back and forth transformation.
138 (insert text)
139 (sgml-quote (point-min) (point-max))
140 (should (string= "Foo&lt;Bar&gt; &#34;Baz&#34; &#39;Qux&#39;\n"
141 (buffer-string)))
142 (sgml-quote (point-min) (point-max) t)
143 (should (string= text (buffer-string)))
144
145 ;; The same text escaped differently.
146 (erase-buffer)
147 (insert "Foo&lt;Bar&gt; &#34;Baz&quot; &#x27;Qux&#X27;\n")
148 (sgml-quote (point-min) (point-max) t)
149 (should (string= text (buffer-string)))
150
151 ;; Lack of semicolon.
152 (erase-buffer)
153 (insert "&amp&amp")
154 (sgml-quote (point-min) (point-max) t)
155 (should (string= "&&" (buffer-string)))
156
157 ;; Double quoting
158 (sgml-quote (point-min) (point-max))
159 (sgml-quote (point-min) (point-max))
160 (sgml-quote (point-min) (point-max) t)
161 (sgml-quote (point-min) (point-max) t)
162 (should (string= "&&" (buffer-string))))))
163
134(provide 'sgml-mode-tests) 164(provide 'sgml-mode-tests)
135;;; sgml-mode-tests.el ends here 165;;; sgml-mode-tests.el ends here