commit 91e4acf7c736dfdb2673dc33c9303b5284e925df (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Mon Apr 13 02:40:56 2020 +0300 Fix hi-lock test and add new test for case-fold (bug#40337) * lisp/hi-lock.el (hi-lock--regexps-at-point): Handle font-lock faces. (hi-lock-unface-buffer): Simplify default value handling. (hi-lock-set-pattern): Add either lighter or regexp to hi-lock-interactive-lighters. (hi-lock-set-pattern): Put overlay prop hi-lock-overlay-regexp to either lighter or regexp. * test/lisp/hi-lock-tests.el (hi-lock-bug26666): Use "b" instead of "a". (hi-lock-case-fold): New test. diff --git a/etc/NEWS b/etc/NEWS index 28c01d71f1..7a7f11f507 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -267,7 +267,7 @@ to substitute spaces in regexp search. --- *** The default value of 'hi-lock-highlight-range' was enlarged. -The new default value is 2000000 (2 million). +The new default value is 2000000 (2 megabytes). ** Texinfo diff --git a/lisp/hi-lock.el b/lisp/hi-lock.el index d5e46651a5..1d8dc0624b 100644 --- a/lisp/hi-lock.el +++ b/lisp/hi-lock.el @@ -564,13 +564,15 @@ in which case the highlighting will not update as you type." (let ((regexp (get-char-property (point) 'hi-lock-overlay-regexp))) (when regexp (push regexp regexps))) ;; With font-locking on, check if the cursor is on a highlighted text. - (let ((face-after (get-text-property (point) 'face)) - (face-before - (unless (bobp) (get-text-property (1- (point)) 'face))) - (faces (mapcar #'hi-lock-keyword->face - hi-lock-interactive-patterns))) - (unless (memq face-before faces) (setq face-before nil)) - (unless (memq face-after faces) (setq face-after nil)) + (let* ((faces-after (get-text-property (point) 'face)) + (faces-before + (unless (bobp) (get-text-property (1- (point)) 'face))) + (faces-after (if (consp faces-after) faces-after (list faces-after))) + (faces-before (if (consp faces-before) faces-before (list faces-before))) + (faces (mapcar #'hi-lock-keyword->face + hi-lock-interactive-patterns)) + (face-after (seq-some (lambda (face) (car (memq face faces))) faces-after)) + (face-before (seq-some (lambda (face) (car (memq face faces))) faces-before))) (when (and face-before face-after (not (eq face-before face-after))) (setq face-before nil)) (when (or face-after face-before) @@ -588,7 +590,8 @@ in which case the highlighting will not update as you type." ;; highlighted text at point. Use this later in ;; during completing-read. (dolist (hi-lock-pattern hi-lock-interactive-patterns) - (let ((regexp (car hi-lock-pattern))) + (let ((regexp (or (car (rassq hi-lock-pattern hi-lock-interactive-lighters)) + (car hi-lock-pattern)))) (if (string-match regexp hi-text) (push regexp regexps))))))) regexps)) @@ -642,15 +645,10 @@ then remove all hi-lock highlighting." (user-error "No highlighting to remove")) ;; Infer the regexp to un-highlight based on cursor position. (let* ((defaults (or (hi-lock--regexps-at-point) - (mapcar #'car hi-lock-interactive-patterns)))) - (setq defaults - (mapcar (lambda (default) - (or (car (rassq default - (mapcar (lambda (a) - (cons (car a) (cadr a))) - hi-lock-interactive-lighters))) - default)) - defaults)) + (mapcar (lambda (pattern) + (or (car (rassq pattern hi-lock-interactive-lighters)) + (car pattern))) + hi-lock-interactive-patterns)))) (list (completing-read (if (null defaults) "Regexp to unhighlight: " @@ -767,7 +765,8 @@ SPACES-REGEXP is a regexp to substitute spaces in font-lock search." (list subexp (list 'quote face) 'prepend))) (no-matches t)) ;; Refuse to highlight a text that is already highlighted. - (if (assoc regexp hi-lock-interactive-patterns) + (if (or (assoc regexp hi-lock-interactive-patterns) + (assoc (or lighter regexp) hi-lock-interactive-lighters)) (add-to-list 'hi-lock--unused-faces (face-name face)) (push pattern hi-lock-interactive-patterns) (push (cons (or lighter regexp) pattern) hi-lock-interactive-lighters) @@ -792,7 +791,7 @@ SPACES-REGEXP is a regexp to substitute spaces in font-lock search." (let ((overlay (make-overlay (match-beginning subexp) (match-end subexp)))) (overlay-put overlay 'hi-lock-overlay t) - (overlay-put overlay 'hi-lock-overlay-regexp regexp) + (overlay-put overlay 'hi-lock-overlay-regexp (or lighter regexp)) (overlay-put overlay 'face face)) (goto-char (match-end 0))) (when no-matches diff --git a/test/lisp/hi-lock-tests.el b/test/lisp/hi-lock-tests.el index dd2c28053a..252caaa265 100644 --- a/test/lisp/hi-lock-tests.el +++ b/test/lisp/hi-lock-tests.el @@ -33,7 +33,9 @@ (car defaults)))) (dotimes (_ 2) (let ((face (hi-lock-read-face-name))) - (hi-lock-set-pattern "a" face)))) + ;; This test should use regexp "b" different from "a" + ;; used in another test because hi-lock--hashcons is global. + (hi-lock-set-pattern "b" face)))) (should (equal hi-lock--unused-faces (cdr faces)))))) (ert-deftest hi-lock-test-set-pattern () @@ -48,5 +50,103 @@ ;; Only one match, then we have used just 1 face (should (equal hi-lock--unused-faces (cdr faces)))))) +(ert-deftest hi-lock-case-fold () + "Test for case-sensitivity." + (let ((hi-lock-auto-select-face t)) + (with-temp-buffer + (insert "a A b B\n") + + (dotimes (_ 2) (highlight-regexp "[a]")) + (should (= (length (overlays-in (point-min) (point-max))) 2)) + (unhighlight-regexp "[a]") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (dotimes (_ 2) (highlight-regexp "[a]" nil nil "a")) + (should (= (length (overlays-in (point-min) (point-max))) 2)) + (unhighlight-regexp "a") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (dotimes (_ 2) (highlight-regexp "[A]" )) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (unhighlight-regexp "[A]") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (dotimes (_ 2) (highlight-regexp "[A]" nil nil "A")) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (unhighlight-regexp "A") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (let ((case-fold-search nil)) (dotimes (_ 2) (highlight-regexp "[a]"))) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (unhighlight-regexp "[a]") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (dotimes (_ 2) (highlight-phrase "a a")) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (unhighlight-regexp "a a") + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (let ((search-spaces-regexp search-whitespace-regexp)) (highlight-regexp "a a")) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll _x _y _z _hist defaults) + (car defaults)))) + (call-interactively 'unhighlight-regexp)) + (should (= (length (overlays-in (point-min) (point-max))) 0)) + + (emacs-lisp-mode) + (setq font-lock-mode t) + + (dotimes (_ 2) (highlight-regexp "[a]")) + (font-lock-ensure) + (should (memq 'hi-yellow (get-text-property 1 'face))) + (should (memq 'hi-yellow (get-text-property 3 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "[a]")) + (should (null (get-text-property 3 'face))) + + (dotimes (_ 2) (highlight-regexp "[a]" nil nil "a")) + (font-lock-ensure) + (should (memq 'hi-yellow (get-text-property 1 'face))) + (should (memq 'hi-yellow (get-text-property 3 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "a")) + (should (null (get-text-property 3 'face))) + + (dotimes (_ 2) (highlight-regexp "[A]" )) + (font-lock-ensure) + (should (null (get-text-property 1 'face))) + (should (memq 'hi-yellow (get-text-property 3 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "[A]")) + (should (null (get-text-property 3 'face))) + + (dotimes (_ 2) (highlight-regexp "[A]" nil nil "A")) + (font-lock-ensure) + (should (null (get-text-property 1 'face))) + (should (memq 'hi-yellow (get-text-property 3 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "A")) + (should (null (get-text-property 3 'face))) + + (let ((case-fold-search nil)) (dotimes (_ 2) (highlight-regexp "[a]"))) + (font-lock-ensure) + (should (memq 'hi-yellow (get-text-property 1 'face))) + (should (null (get-text-property 3 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "[a]")) + (should (null (get-text-property 1 'face))) + + (dotimes (_ 2) (highlight-phrase "a a")) + (font-lock-ensure) + (should (memq 'hi-yellow (get-text-property 1 'face))) + (let ((font-lock-fontified t)) (unhighlight-regexp "a a")) + (should (null (get-text-property 1 'face))) + + (let ((search-spaces-regexp search-whitespace-regexp)) (highlight-regexp "a a")) + (font-lock-ensure) + (should (memq 'hi-yellow (get-text-property 1 'face))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll _x _y _z _hist defaults) + (car defaults))) + (font-lock-fontified t)) + (call-interactively 'unhighlight-regexp)) + (should (null (get-text-property 1 'face)))))) + (provide 'hi-lock-tests) ;;; hi-lock-tests.el ends here commit 68ffe4a3c9a001db528b057109d11de71471e4ff Author: Štěpán Němec Date: Sun Apr 12 18:20:41 2020 +0200 Fix bootstrap compiler warnings about `read-library-name' Introduced by 2020-03-28T22:16:28+01:00!stepnem@gmail.com 2c45091791 (load-library, locate-library: Use read-library-name) Thanks to Juanma Barranquero for reporting. * lisp/files.el: * lisp/subr.el: Declare 'read-library-name'. diff --git a/lisp/files.el b/lisp/files.el index beafdaca99..f49be4f21d 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1094,6 +1094,8 @@ REMOTE is non-nil, search on the remote host indicated by (let ((default-directory (file-name-quote default-directory 'top))) (locate-file command exec-path exec-suffixes 1)))) +(declare-function read-library-name "find-func" nil) + (defun load-library (library) "Load the Emacs Lisp library named LIBRARY. LIBRARY should be a string. diff --git a/lisp/subr.el b/lisp/subr.el index 70a74fba66..f7445d8c25 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2285,6 +2285,8 @@ Otherwise TYPE is assumed to be a symbol property." (not (eq 'require (car match))))))) (throw 'found file)))))) +(declare-function read-library-name "find-func" nil) + (defun locate-library (library &optional nosuffix path interactive-call) "Show the precise file name of Emacs library LIBRARY. LIBRARY should be a relative file name of the library, a string. commit 42306747d8dece897805e89c36c3741bfb8d5e7c Author: Philipp Stephani Date: Sun Apr 12 19:04:11 2020 +0200 Fix error in 'call-process-region' when START is nil (Bug#40576) * src/callproc.c (Fcall_process_region): Fix behavior when START is nil and DELETE is non-nil. * test/src/callproc-tests.el (call-process-region-entire-buffer-with-delete): New unit test. diff --git a/src/callproc.c b/src/callproc.c index 8883415f3f..65c858393a 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1099,7 +1099,17 @@ usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &r } if (nargs > 3 && !NILP (args[3])) - Fdelete_region (start, end); + { + if (NILP (start)) + { + /* No need to save restrictions since we delete everything + anyway. */ + Fwiden (); + del_range (BEG, Z); + } + else + Fdelete_region (start, end); + } if (nargs > 3) { diff --git a/test/src/callproc-tests.el b/test/src/callproc-tests.el index bf7d47b27f..1617d5e33d 100644 --- a/test/src/callproc-tests.el +++ b/test/src/callproc-tests.el @@ -66,4 +66,14 @@ (error :got-error)))) (should have-called-debugger))) +(ert-deftest call-process-region-entire-buffer-with-delete () + "Check that Bug#40576 is fixed." + (let ((emacs (expand-file-name invocation-name invocation-directory))) + (skip-unless (file-executable-p emacs)) + (with-temp-buffer + (insert "Buffer contents\n") + (should + (eq (call-process-region nil nil emacs :delete nil nil "--version") 0)) + (should (eq (buffer-size) 0))))) + ;;; callproc-tests.el ends here commit 900947fbe8b202ce2ae15e87ef377ca27da73ec9 Author: Philipp Stephani Date: Sun Apr 12 18:08:04 2020 +0200 ; * test/src/callproc-tests.el: Fix checkdoc errors. diff --git a/test/src/callproc-tests.el b/test/src/callproc-tests.el index 39d2014488..bf7d47b27f 100644 --- a/test/src/callproc-tests.el +++ b/test/src/callproc-tests.el @@ -17,6 +17,11 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; Unit tests for src/callproc.c. + ;;; Code: (require 'ert) @@ -60,3 +65,5 @@ (call-process "c:/nul.exe") (error :got-error)))) (should have-called-debugger))) + +;;; callproc-tests.el ends here commit aed427ece5c6e78633e08fd599d0cadeeb3d8d58 Author: Philipp Stephani Date: Sun Apr 12 14:25:33 2020 +0200 Also use named functions for the ‘gv’ declare forms (Bug#40491) * lisp/emacs-lisp/gv.el (gv--expander-defun-declaration) (gv--setter-defun-declaration): New helper functions; use them. diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el index b43e53b9d2..3ab49293e9 100644 --- a/lisp/emacs-lisp/gv.el +++ b/lisp/emacs-lisp/gv.el @@ -166,15 +166,25 @@ arguments as NAME. DO is a function as defined in `gv-get'." ;; (`(expand ,expander) `(gv-define-expand ,name ,expander)) (_ (message "Unknown %s declaration %S" symbol handler) nil)))) +;; Additions for `declare'. We specify the values as named aliases so +;; that `describe-variable' prints something useful; cf. Bug#40491. + +;;;###autoload +(defsubst gv--expander-defun-declaration (&rest args) + (apply #'gv--defun-declaration 'gv-expander args)) + +;;;###autoload +(defsubst gv--setter-defun-declaration (&rest args) + (apply #'gv--defun-declaration 'gv-setter args)) + ;;;###autoload (or (assq 'gv-expander defun-declarations-alist) - (let ((x `(gv-expander - ,(apply-partially #'gv--defun-declaration 'gv-expander)))) + (let ((x (list 'gv-expander #'gv--expander-defun-declaration))) (push x macro-declarations-alist) (push x defun-declarations-alist))) ;;;###autoload (or (assq 'gv-setter defun-declarations-alist) - (push `(gv-setter ,(apply-partially #'gv--defun-declaration 'gv-setter)) + (push (list 'gv-setter #'gv--setter-defun-declaration) defun-declarations-alist)) ;; (defmacro gv-define-expand (name expander) commit 4f197a5e79ef25bcbcb3bf50ab3071fd0f1fae9e Author: Philipp Stephani Date: Sun Apr 12 12:01:47 2020 +0200 Use named functions in {defun,macro}-declarations-alist (Bug#40491) * lisp/emacs-lisp/byte-run.el (byte-run--set-advertised-calling-convention) (byte-run--set-obsolete, byte-run--set-interactive-only) (byte-run--set-pure, byte-run--set-side-effect-free) (byte-run--set-compiler-macro, byte-run--set-doc-string) (byte-run--set-indent, byte-run--set-debug) (byte-run--set-no-font-lock-keyword): New helper functions. (defun-declarations-alist, macro-declarations-alist): Use them. diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el index 6a49c60099..fa769adb06 100644 --- a/lisp/emacs-lisp/byte-run.el +++ b/lisp/emacs-lisp/byte-run.el @@ -82,65 +82,84 @@ The return value of this function is not used." ;; We define macro-declaration-alist here because it is needed to ;; handle declarations in macro definitions and this is the first file -;; loaded by loadup.el that uses declarations in macros. +;; loaded by loadup.el that uses declarations in macros. We specify +;; the values as named aliases so that `describe-variable' prints +;; something useful; cf. Bug#40491. We can only use backquotes inside +;; the lambdas and not for those properties that are used by functions +;; loaded before backquote.el. + +(defalias 'byte-run--set-advertised-calling-convention + #'(lambda (f _args arglist when) + (list 'set-advertised-calling-convention + (list 'quote f) (list 'quote arglist) (list 'quote when)))) + +(defalias 'byte-run--set-obsolete + #'(lambda (f _args new-name when) + (list 'make-obsolete + (list 'quote f) (list 'quote new-name) (list 'quote when)))) + +(defalias 'byte-run--set-interactive-only + #'(lambda (f _args instead) + (list 'function-put (list 'quote f) + ''interactive-only (list 'quote instead)))) + +(defalias 'byte-run--set-pure + #'(lambda (f _args val) + (list 'function-put (list 'quote f) + ''pure (list 'quote val)))) + +(defalias 'byte-run--set-side-effect-free + #'(lambda (f _args val) + (list 'function-put (list 'quote f) + ''side-effect-free (list 'quote val)))) + +(defalias 'byte-run--set-compiler-macro + #'(lambda (f args compiler-function) + (if (not (eq (car-safe compiler-function) 'lambda)) + `(eval-and-compile + (function-put ',f 'compiler-macro #',compiler-function)) + (let ((cfname (intern (concat (symbol-name f) "--anon-cmacro"))) + ;; Avoid cadr/cddr so we can use `compiler-macro' before + ;; defining cadr/cddr. + (data (cdr compiler-function))) + `(progn + (eval-and-compile + (function-put ',f 'compiler-macro #',cfname)) + ;; Don't autoload the compiler-macro itself, since the + ;; macroexpander will find this file via `f's autoload, + ;; if needed. + :autoload-end + (eval-and-compile + (defun ,cfname (,@(car data) ,@args) + ,@(cdr data)))))))) + +(defalias 'byte-run--set-doc-string + #'(lambda (f _args pos) + (list 'function-put (list 'quote f) + ''doc-string-elt (list 'quote pos)))) + +(defalias 'byte-run--set-indent + #'(lambda (f _args val) + (list 'function-put (list 'quote f) + ''lisp-indent-function (list 'quote val)))) ;; Add any new entries to info node `(elisp)Declare Form'. (defvar defun-declarations-alist (list - ;; We can only use backquotes inside the lambdas and not for those - ;; properties that are used by functions loaded before backquote.el. (list 'advertised-calling-convention - #'(lambda (f _args arglist when) - (list 'set-advertised-calling-convention - (list 'quote f) (list 'quote arglist) (list 'quote when)))) - (list 'obsolete - #'(lambda (f _args new-name when) - (list 'make-obsolete - (list 'quote f) (list 'quote new-name) (list 'quote when)))) - (list 'interactive-only - #'(lambda (f _args instead) - (list 'function-put (list 'quote f) - ''interactive-only (list 'quote instead)))) + #'byte-run--set-advertised-calling-convention) + (list 'obsolete #'byte-run--set-obsolete) + (list 'interactive-only #'byte-run--set-interactive-only) ;; FIXME: Merge `pure' and `side-effect-free'. - (list 'pure - #'(lambda (f _args val) - (list 'function-put (list 'quote f) - ''pure (list 'quote val))) + (list 'pure #'byte-run--set-pure "If non-nil, the compiler can replace calls with their return value. This may shift errors from run-time to compile-time.") - (list 'side-effect-free - #'(lambda (f _args val) - (list 'function-put (list 'quote f) - ''side-effect-free (list 'quote val))) + (list 'side-effect-free #'byte-run--set-side-effect-free "If non-nil, calls can be ignored if their value is unused. If `error-free', drop calls even if `byte-compile-delete-errors' is nil.") - (list 'compiler-macro - #'(lambda (f args compiler-function) - (if (not (eq (car-safe compiler-function) 'lambda)) - `(eval-and-compile - (function-put ',f 'compiler-macro #',compiler-function)) - (let ((cfname (intern (concat (symbol-name f) "--anon-cmacro"))) - ;; Avoid cadr/cddr so we can use `compiler-macro' before - ;; defining cadr/cddr. - (data (cdr compiler-function))) - `(progn - (eval-and-compile - (function-put ',f 'compiler-macro #',cfname)) - ;; Don't autoload the compiler-macro itself, since the - ;; macroexpander will find this file via `f's autoload, - ;; if needed. - :autoload-end - (eval-and-compile - (defun ,cfname (,@(car data) ,@args) - ,@(cdr data)))))))) - (list 'doc-string - #'(lambda (f _args pos) - (list 'function-put (list 'quote f) - ''doc-string-elt (list 'quote pos)))) - (list 'indent - #'(lambda (f _args val) - (list 'function-put (list 'quote f) - ''lisp-indent-function (list 'quote val))))) + (list 'compiler-macro #'byte-run--set-compiler-macro) + (list 'doc-string #'byte-run--set-doc-string) + (list 'indent #'byte-run--set-indent)) "List associating function properties to their macro expansion. Each element of the list takes the form (PROP FUN) where FUN is a function. For each (PROP . VALUES) in a function's declaration, @@ -150,18 +169,22 @@ to set this property. This is used by `declare'.") +(defalias 'byte-run--set-debug + #'(lambda (name _args spec) + (list 'progn :autoload-end + (list 'put (list 'quote name) + ''edebug-form-spec (list 'quote spec))))) + +(defalias 'byte-run--set-no-font-lock-keyword + #'(lambda (name _args val) + (list 'function-put (list 'quote name) + ''no-font-lock-keyword (list 'quote val)))) + (defvar macro-declarations-alist (cons - (list 'debug - #'(lambda (name _args spec) - (list 'progn :autoload-end - (list 'put (list 'quote name) - ''edebug-form-spec (list 'quote spec))))) + (list 'debug #'byte-run--set-debug) (cons - (list 'no-font-lock-keyword - #'(lambda (name _args val) - (list 'function-put (list 'quote name) - ''no-font-lock-keyword (list 'quote val)))) + (list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword) defun-declarations-alist)) "List associating properties of macros to their macro expansion. Each element of the list takes the form (PROP FUN) where FUN is a function. commit c7ecc6bbc03af4c2746e2e8765dbbe5bf4a3a908 Author: Philipp Stephani Date: Sun Apr 12 11:58:36 2020 +0200 Fix a bootstrap issue with unescaped character literal detection. * src/lread.c (load_warn_unescaped_character_literals): Deal with the case that 'byte-run--unescaped-character-literals-warning' isn't yet defined. diff --git a/src/lread.c b/src/lread.c index eabf3b938c..8ec9e32504 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1030,7 +1030,13 @@ load_error_handler (Lisp_Object data) static void load_warn_unescaped_character_literals (Lisp_Object file) { - Lisp_Object warning = call0 (Qbyte_run_unescaped_character_literals_warning); + Lisp_Object function + = Fsymbol_function (Qbyte_run_unescaped_character_literals_warning); + /* If byte-run.el is being loaded, + `byte-run--unescaped-character-literals-warning' isn't yet + defined. Since it'll be byte-compiled later, ignore potential + unescaped character literals. */ + Lisp_Object warning = NILP (function) ? Qnil : call0 (function); if (!NILP (warning)) { AUTO_STRING (format, "Loading `%s': %s");