diff options
author | Andrea Corallo <akrl@sdf.org> | 2020-10-31 15:00:00 +0000 |
---|---|---|
committer | Andrea Corallo <akrl@sdf.org> | 2020-10-31 15:00:00 +0000 |
commit | f7f5d59ab4c4cc1a7db46d7f1d462655254e1a87 (patch) | |
tree | 33b8af817af32d9414fe3aa08f22c4ce2aa4dc38 /lisp/emacs-lisp | |
parent | fd9e9308d27138a16e2e93417bd7ad4448fea40a (diff) | |
parent | 283b8d274bd54192b3876ce8bf2930a096391839 (diff) | |
download | emacs-f7f5d59ab4c4cc1a7db46d7f1d462655254e1a87.tar.gz emacs-f7f5d59ab4c4cc1a7db46d7f1d462655254e1a87.tar.bz2 emacs-f7f5d59ab4c4cc1a7db46d7f1d462655254e1a87.zip |
Merge remote-tracking branch 'savannah/master' into HEAD
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r-- | lisp/emacs-lisp/bindat.el | 174 | ||||
-rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 14 | ||||
-rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 3 | ||||
-rw-r--r-- | lisp/emacs-lisp/checkdoc.el | 2 | ||||
-rw-r--r-- | lisp/emacs-lisp/copyright.el | 9 | ||||
-rw-r--r-- | lisp/emacs-lisp/easy-mmode.el | 103 | ||||
-rw-r--r-- | lisp/emacs-lisp/edebug.el | 17 | ||||
-rw-r--r-- | lisp/emacs-lisp/eldoc.el | 406 | ||||
-rw-r--r-- | lisp/emacs-lisp/ert.el | 3 | ||||
-rw-r--r-- | lisp/emacs-lisp/shortdoc.el | 211 | ||||
-rw-r--r-- | lisp/emacs-lisp/text-property-search.el | 12 | ||||
-rw-r--r-- | lisp/emacs-lisp/timer-list.el | 38 | ||||
-rw-r--r-- | lisp/emacs-lisp/unsafep.el | 32 |
13 files changed, 670 insertions, 354 deletions
diff --git a/lisp/emacs-lisp/bindat.el b/lisp/emacs-lisp/bindat.el index 95581c40a46..0fd273aa3e3 100644 --- a/lisp/emacs-lisp/bindat.el +++ b/lisp/emacs-lisp/bindat.el @@ -1,4 +1,4 @@ -;;; bindat.el --- binary data structure packing and unpacking. -*- lexical-binding: t -*- +;;; bindat.el --- binary data structure packing and unpacking. ;; Copyright (C) 2002-2020 Free Software Foundation, Inc. @@ -193,8 +193,8 @@ ;; Helper functions for structure unpacking. ;; Relies on dynamic binding of BINDAT-RAW and BINDAT-IDX -(defvar bindat-raw nil) -(defvar bindat-idx nil) +(defvar bindat-raw) +(defvar bindat-idx) (defun bindat--unpack-u8 () (prog1 @@ -276,7 +276,7 @@ (t nil))) (defun bindat--unpack-group (spec) - (let (struct) + (let (struct last) (while spec (let* ((item (car spec)) (field (car item)) @@ -330,21 +330,21 @@ (setq data (bindat--unpack-group (cdr case)) cases nil))))) (t - (setq data (bindat--unpack-item type len vectype)))) + (setq data (bindat--unpack-item type len vectype) + last data))) (if data (if field (setq struct (cons (cons field data) struct)) (setq struct (append data struct)))))) struct)) -(defun bindat-unpack (spec raw &optional idx) - "Return structured data according to SPEC for binary data in RAW. -RAW is a unibyte string or vector. -Optional third arg IDX specifies the starting offset in RAW." - (when (multibyte-string-p raw) +(defun bindat-unpack (spec bindat-raw &optional bindat-idx) + "Return structured data according to SPEC for binary data in BINDAT-RAW. +BINDAT-RAW is a unibyte string or vector. +Optional third arg BINDAT-IDX specifies the starting offset in BINDAT-RAW." + (when (multibyte-string-p bindat-raw) (error "String is multibyte")) - (setq bindat-raw raw) - (setq bindat-idx (or idx 0)) + (unless bindat-idx (setq bindat-idx 0)) (bindat--unpack-group spec)) (defun bindat-get-field (struct &rest field) @@ -373,70 +373,74 @@ e.g. corresponding to STRUCT.FIELD1[INDEX2].FIELD3..." (ip . 4))) (defun bindat--length-group (struct spec) - (while spec - (let* ((item (car spec)) - (field (car item)) - (type (nth 1 item)) - (len (nth 2 item)) - (vectype (and (eq type 'vec) (nth 3 item))) - (tail 3)) - (setq spec (cdr spec)) - (if (and (consp field) (eq (car field) 'eval)) - (setq field (eval (car (cdr field))))) - (if (and type (consp type) (eq (car type) 'eval)) - (setq type (eval (car (cdr type))))) - (if (and len (consp len) (eq (car len) 'eval)) - (setq len (eval (car (cdr len))))) - (if (memq field '(eval fill align struct union)) - (setq tail 2 - len type - type field - field nil)) - (if (and (consp len) (not (eq type 'eval))) - (setq len (apply #'bindat-get-field struct len))) - (if (not len) - (setq len 1)) - (while (eq type 'vec) - (if (consp vectype) - (setq len (* len (nth 1 vectype)) - type (nth 2 vectype)) - (setq type (or vectype 'u8) - vectype nil))) - (cond - ((eq type 'eval) - (if field - (setq struct (cons (cons field (eval len)) struct)) - (eval len))) - ((eq type 'fill) - (setq bindat-idx (+ bindat-idx len))) - ((eq type 'align) - (while (/= (% bindat-idx len) 0) - (setq bindat-idx (1+ bindat-idx)))) - ((eq type 'struct) - (bindat--length-group - (if field (bindat-get-field struct field) struct) (eval len))) - ((eq type 'repeat) - (let ((index 0) (count len)) - (while (< index count) - (bindat--length-group - (nth index (bindat-get-field struct field)) - (nthcdr tail item)) - (setq index (1+ index))))) - ((eq type 'union) - (let ((tag len) (cases (nthcdr tail item)) case cc) - (while cases - (setq case (car cases) - cases (cdr cases) - cc (car case)) - (if (or (equal cc tag) (equal cc t) - (and (consp cc) (eval cc))) - (progn - (bindat--length-group struct (cdr case)) - (setq cases nil)))))) - (t - (if (setq type (assq type bindat--fixed-length-alist)) - (setq len (* len (cdr type)))) - (setq bindat-idx (+ bindat-idx len))))))) + (let (last) + (while spec + (let* ((item (car spec)) + (field (car item)) + (type (nth 1 item)) + (len (nth 2 item)) + (vectype (and (eq type 'vec) (nth 3 item))) + (tail 3)) + (setq spec (cdr spec)) + (if (and (consp field) (eq (car field) 'eval)) + (setq field (eval (car (cdr field))))) + (if (and type (consp type) (eq (car type) 'eval)) + (setq type (eval (car (cdr type))))) + (if (and len (consp len) (eq (car len) 'eval)) + (setq len (eval (car (cdr len))))) + (if (memq field '(eval fill align struct union)) + (setq tail 2 + len type + type field + field nil)) + (if (and (consp len) (not (eq type 'eval))) + (setq len (apply 'bindat-get-field struct len))) + (if (not len) + (setq len 1)) + (while (eq type 'vec) + (let ((vlen 1)) + (if (consp vectype) + (setq len (* len (nth 1 vectype)) + type (nth 2 vectype)) + (setq type (or vectype 'u8) + vectype nil)))) + (cond + ((eq type 'eval) + (if field + (setq struct (cons (cons field (eval len)) struct)) + (eval len))) + ((eq type 'fill) + (setq bindat-idx (+ bindat-idx len))) + ((eq type 'align) + (while (/= (% bindat-idx len) 0) + (setq bindat-idx (1+ bindat-idx)))) + ((eq type 'struct) + (bindat--length-group + (if field (bindat-get-field struct field) struct) (eval len))) + ((eq type 'repeat) + (let ((index 0) (count len)) + (while (< index count) + (bindat--length-group + (nth index (bindat-get-field struct field)) + (nthcdr tail item)) + (setq index (1+ index))))) + ((eq type 'union) + (let ((tag len) (cases (nthcdr tail item)) case cc) + (while cases + (setq case (car cases) + cases (cdr cases) + cc (car case)) + (if (or (equal cc tag) (equal cc t) + (and (consp cc) (eval cc))) + (progn + (bindat--length-group struct (cdr case)) + (setq cases nil)))))) + (t + (if (setq type (assq type bindat--fixed-length-alist)) + (setq len (* len (cdr type)))) + (if field + (setq last (bindat-get-field struct field))) + (setq bindat-idx (+ bindat-idx len)))))))) (defun bindat-length (spec struct) "Calculate bindat-raw length for STRUCT according to bindat SPEC." @@ -592,17 +596,17 @@ e.g. corresponding to STRUCT.FIELD1[INDEX2].FIELD3..." (bindat--pack-item last type len vectype) )))))) -(defun bindat-pack (spec struct &optional raw idx) +(defun bindat-pack (spec struct &optional bindat-raw bindat-idx) "Return binary data packed according to SPEC for structured data STRUCT. -Optional third arg RAW is a pre-allocated unibyte string or -vector to pack into. -Optional fourth arg IDX is the starting offset into BINDAT-RAW." - (when (multibyte-string-p raw) +Optional third arg BINDAT-RAW is a pre-allocated unibyte string or vector to +pack into. +Optional fourth arg BINDAT-IDX is the starting offset into BINDAT-RAW." + (when (multibyte-string-p bindat-raw) (error "Pre-allocated string is multibyte")) - (let ((no-return raw)) - (setq bindat-idx (or idx 0)) - (setq bindat-raw (or raw - (make-string (+ bindat-idx (bindat-length spec struct)) 0))) + (let ((no-return bindat-raw)) + (unless bindat-idx (setq bindat-idx 0)) + (unless bindat-raw + (setq bindat-raw (make-string (+ bindat-idx (bindat-length spec struct)) 0))) (bindat--pack-group struct spec) (if no-return nil bindat-raw))) diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 65e4e446266..1dc83dd3958 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -830,11 +830,13 @@ (defun byte-optimize-assoc (form) ;; Replace 2-argument `assoc' with `assq', `rassoc' with `rassq', ;; if the first arg is a symbol. - (if (and (= (length form) 3) - (byte-optimize--constant-symbol-p (nth 1 form))) - (cons (if (eq (car form) 'assoc) 'assq 'rassq) - (cdr form)) - form)) + (cond + ((/= (length form) 3) + form) + ((byte-optimize--constant-symbol-p (nth 1 form)) + (cons (if (eq (car form) 'assoc) 'assq 'rassq) + (cdr form))) + (t (byte-optimize-constant-args form)))) (defun byte-optimize-memq (form) ;; (memq foo '(bar)) => (and (eq foo 'bar) '(bar)) @@ -1144,7 +1146,7 @@ ;; I wonder if I missed any :-\) (let ((side-effect-free-fns '(% * + - / /= 1+ 1- < <= = > >= abs acos append aref ash asin atan - assoc assq + assq boundp buffer-file-name buffer-local-variables buffer-modified-p buffer-substring byte-code-function-p capitalize car-less-than-car car cdr ceiling char-after char-before diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 1e2ce8cd130..a3c830e60dd 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -1989,7 +1989,7 @@ See also `emacs-lisp-byte-compile-and-load'." (byte-compile--known-dynamic-vars (byte-compile--load-dynvars (getenv "EMACS_DYNVARS_FILE"))) target-file input-buffer output-buffer - byte-compile-dest-file) + byte-compile-dest-file byte-compiler-error-flag) (setq target-file (byte-compile-dest-file filename)) (setq byte-compile-dest-file target-file) (with-current-buffer @@ -2051,7 +2051,6 @@ See also `emacs-lisp-byte-compile-and-load'." 'no-byte-compile) (when byte-compile-verbose (message "Compiling %s..." filename)) - (setq byte-compiler-error-flag nil) ;; It is important that input-buffer not be current at this call, ;; so that the value of point set in input-buffer ;; within byte-compile-from-buffer lingers in that buffer. diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 23121c245ef..a485378a926 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -2589,7 +2589,7 @@ This function will not modify `match-data'." ;; going on. (if checkdoc-bouncy-flag (message "%s -> done" question)) (delete-region start end) - (insert replacewith) + (insert-before-markers replacewith) (if checkdoc-bouncy-flag (sit-for 0)) (setq ret t))) (delete-overlay o) diff --git a/lisp/emacs-lisp/copyright.el b/lisp/emacs-lisp/copyright.el index 6fa51c3f644..9828ca63ebc 100644 --- a/lisp/emacs-lisp/copyright.el +++ b/lisp/emacs-lisp/copyright.el @@ -1,4 +1,4 @@ -;;; copyright.el --- update the copyright notice in current buffer +;;; copyright.el --- update the copyright notice in current buffer -*- lexical-binding: t -*- ;; Copyright (C) 1991-1995, 1998, 2001-2020 Free Software Foundation, ;; Inc. @@ -37,14 +37,12 @@ (defcustom copyright-limit 2000 "Don't try to update copyright beyond this position unless interactive. A value of nil means to search whole buffer." - :group 'copyright :type '(choice (integer :tag "Limit") (const :tag "No limit"))) (defcustom copyright-at-end-flag nil "Non-nil means to search backwards from the end of the buffer for copyright. This is useful for ChangeLogs." - :group 'copyright :type 'boolean :version "23.1") ;;;###autoload(put 'copyright-at-end-flag 'safe-local-variable 'booleanp) @@ -56,7 +54,6 @@ This is useful for ChangeLogs." \\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)" "What your copyright notice looks like. The second \\( \\) construct must match the years." - :group 'copyright :type 'regexp) (defcustom copyright-names-regexp "" @@ -64,7 +61,6 @@ The second \\( \\) construct must match the years." Only copyright lines where the name matches this regexp will be updated. This allows you to avoid adding years to a copyright notice belonging to someone else or to a group for which you do not work." - :group 'copyright :type 'regexp) ;; The worst that can happen is a malicious regexp that overflows in @@ -76,7 +72,6 @@ someone else or to a group for which you do not work." "\\(\\s *\\)\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)" "Match additional copyright notice years. The second \\( \\) construct must match the years." - :group 'copyright :type 'regexp) ;; See "Copyright Notices" in maintain.info. @@ -87,7 +82,6 @@ The second \\( \\) construct must match the years." For example: 2005, 2006, 2007, 2008 might be replaced with 2005-2008. If you use ranges, you should add an explanatory note in a README file. The function `copyright-fix-years' respects this variable." - :group 'copyright :type 'boolean :version "24.1") @@ -96,7 +90,6 @@ The function `copyright-fix-years' respects this variable." (defcustom copyright-query 'function "If non-nil, ask user before changing copyright. When this is `function', only ask when called non-interactively." - :group 'copyright :type '(choice (const :tag "Do not ask") (const :tag "Ask unless interactive" function) (other :tag "Ask" t))) diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 8c1e5b227a6..a707d204f8b 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -375,18 +375,21 @@ No problems result if this variable is not bound. (defmacro define-globalized-minor-mode (global-mode mode turn-on &rest body) "Make a global mode GLOBAL-MODE corresponding to buffer-local minor MODE. TURN-ON is a function that will be called with no args in every buffer - and that should try to turn MODE on if applicable for that buffer. -Each of KEY VALUE is a pair of CL-style keyword arguments. As - the minor mode defined by this function is always global, any - :global keyword is ignored. Other keywords have the same - meaning as in `define-minor-mode', which see. In particular, - :group specifies the custom group. The most useful keywords - are those that are passed on to the `defcustom'. It normally - makes no sense to pass the :lighter or :keymap keywords to - `define-globalized-minor-mode', since these are usually passed - to the buffer-local version of the minor mode. +and that should try to turn MODE on if applicable for that buffer. + +Each of KEY VALUE is a pair of CL-style keyword arguments. :predicate +specifies which major modes the globalized minor mode should be switched on +in. As the minor mode defined by this function is always global, any +:global keyword is ignored. Other keywords have the same meaning as in +`define-minor-mode', which see. In particular, :group specifies the custom +group. The most useful keywords are those that are passed on to the +`defcustom'. It normally makes no sense to pass the :lighter or :keymap +keywords to `define-globalized-minor-mode', since these are usually passed +to the buffer-local version of the minor mode. + BODY contains code to execute each time the mode is enabled or disabled. - It is executed after toggling the mode, and before running GLOBAL-MODE-hook. +It is executed after toggling the mode, and before running +GLOBAL-MODE-hook. If MODE's set-up depends on the major mode in effect when it was enabled, then disabling and reenabling MODE should make MODE work @@ -415,7 +418,11 @@ on if the hook has explicitly disabled it. (minor-MODE-hook (intern (concat mode-name "-hook"))) (MODE-set-explicitly (intern (concat mode-name "-set-explicitly"))) (MODE-major-mode (intern (concat (symbol-name mode) "-major-mode"))) - keyw) + (MODE-predicate (intern (concat (replace-regexp-in-string + "-mode\\'" "" global-mode-name) + "-modes"))) + (turn-on-function `#',turn-on) + keyw predicate) ;; Check keys. (while (keywordp (setq keyw (car body))) @@ -423,6 +430,13 @@ on if the hook has explicitly disabled it. (pcase keyw (:group (setq group (nconc group (list :group (pop body))))) (:global (pop body)) + (:predicate + (setq predicate (list (pop body))) + (setq turn-on-function + `(lambda () + (require 'easy-mmode) + (when (easy-mmode--globalized-predicate-p ,(car predicate)) + (funcall ,turn-on-function))))) (_ (push keyw extra-keywords) (push (pop body) extra-keywords)))) `(progn @@ -442,10 +456,17 @@ ARG is omitted or nil. %s is enabled in all buffers where `%s' would do it. -See `%s' for more information on %s." + +See `%s' for more information on +%s.%s" pretty-name pretty-global-name - pretty-name turn-on mode pretty-name) - :global t ,@group ,@(nreverse extra-keywords) + pretty-name turn-on mode pretty-name + (if predicate + (format "\n\n`%s' is used to control which modes +this minor mode is used in." + MODE-predicate) + "")) + :global t ,@group ,@(nreverse extra-keywords) ;; Setup hook to handle future mode changes and new buffers. (if ,global-mode @@ -461,9 +482,28 @@ See `%s' for more information on %s." ;; Go through existing buffers. (dolist (buf (buffer-list)) (with-current-buffer buf - (if ,global-mode (funcall #',turn-on) (when ,mode (,mode -1))))) + (if ,global-mode (funcall ,turn-on-function) + (when ,mode (,mode -1))))) ,@body) + ,(when predicate + `(defcustom ,MODE-predicate ,(car predicate) + ,(format "Which major modes `%s' is switched on in. +This variable can be either t (all major modes), nil (no major modes), +or a list of modes and (not modes) to switch use this minor mode or +not. For instance + + (c-mode (not message-mode mail-mode) text-mode) + +means \"use this mode in all modes derived from `c-mode', don't use in +modes derived from `message-mode' or `mail-mode', but do use in other +modes derived from `text-mode'\". An element with value t means \"use\" +and nil means \"don't use\". There's an implicit nil at the end of the +list." + mode) + :type '(repeat sexp) + :group ,group)) + ;; Autoloading define-globalized-minor-mode autoloads everything ;; up-to-here. :autoload-end @@ -497,8 +537,8 @@ See `%s' for more information on %s." (if ,mode (progn (,mode -1) - (funcall #',turn-on)) - (funcall #',turn-on)))) + (funcall ,turn-on-function)) + (funcall ,turn-on-function)))) (setq ,MODE-major-mode major-mode)))))) (put ',MODE-enable-in-buffers 'definition-name ',global-mode) @@ -513,6 +553,33 @@ See `%s' for more information on %s." (add-hook 'post-command-hook ',MODE-check-buffers)) (put ',MODE-cmhh 'definition-name ',global-mode)))) +(defun easy-mmode--globalized-predicate-p (predicate) + (cond + ((eq predicate t) + t) + ((eq predicate nil) + nil) + ((listp predicate) + ;; Legacy support for (not a b c). + (when (eq (car predicate) 'not) + (setq predicate (nconc (mapcar (lambda (e) (list 'not e)) + (cdr predicate)) + (list t)))) + (catch 'found + (dolist (elem predicate) + (cond + ((eq elem t) + (throw 'found t)) + ((eq elem nil) + (throw 'found nil)) + ((and (consp elem) + (eq (car elem) 'not)) + (when (apply #'derived-mode-p (cdr elem)) + (throw 'found nil))) + ((symbolp elem) + (when (derived-mode-p elem) + (throw 'found t))))))))) + ;;; ;;; easy-mmode-defmap ;;; diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index f10dc13fa07..fe733630bad 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el @@ -2665,9 +2665,6 @@ See `edebug-behavior-alist' for implementations.") (defvar edebug-previous-result nil) ;; Last result returned. -;; Emacs 19 adds an arg to mark and mark-marker. -(defalias 'edebug-mark-marker 'mark-marker) - (defun edebug--display (value offset-index arg-mode) ;; edebug--display-1 is too big, we should split it. This function ;; here was just introduced to avoid making edebug--display-1 @@ -2895,8 +2892,8 @@ See `edebug-behavior-alist' for implementations.") ;; But don't restore point if edebug-buffer is current buffer. (if (not (eq edebug-buffer edebug-outside-buffer)) (goto-char edebug-outside-point)) - (if (marker-buffer (edebug-mark-marker)) - (set-marker (edebug-mark-marker) edebug-outside-mark)) + (if (marker-buffer (mark-marker)) + (set-marker (mark-marker) edebug-outside-mark)) )) ; unwind-protect ;; None of the following is done if quit or signal occurs. @@ -3153,8 +3150,8 @@ before returning. The default is one second." (goto-char edebug-outside-point) (message "Current buffer: %s Point: %s Mark: %s" (current-buffer) (point) - (if (marker-buffer (edebug-mark-marker)) - (marker-position (edebug-mark-marker)) "<not set>")) + (if (marker-buffer (mark-marker)) + (marker-position (mark-marker)) "<not set>")) (sit-for arg) (edebug-pop-to-buffer edebug-buffer (car edebug-window-data))))) @@ -3725,8 +3722,8 @@ Return the result of the last expression." ;; for us. (with-current-buffer edebug-outside-buffer ; of edebug-buffer (goto-char edebug-outside-point) - (if (marker-buffer (edebug-mark-marker)) - (set-marker (edebug-mark-marker) edebug-outside-mark)) + (if (marker-buffer (mark-marker)) + (set-marker (mark-marker) edebug-outside-mark)) ,@body) ;; Back to edebug-buffer. Restore rest of inside context. @@ -4667,5 +4664,7 @@ instrumentation for, defaulting to all functions." (message "Removed edebug instrumentation from %s" (mapconcat #'symbol-name functions ", "))) +(define-obsolete-function-alias 'edebug-mark-marker #'mark-marker "28.1") + (provide 'edebug) ;;; edebug.el ends here diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el index 9e38e5908ed..78cb8f08c34 100644 --- a/lisp/emacs-lisp/eldoc.el +++ b/lisp/emacs-lisp/eldoc.el @@ -5,7 +5,7 @@ ;; Author: Noah Friedman <friedman@splode.com> ;; Keywords: extensions ;; Created: 1995-10-06 -;; Version: 1.10.0 +;; Version: 1.11.0 ;; Package-Requires: ((emacs "26.3")) ;; This is a GNU ELPA :core package. Avoid functionality that is not @@ -67,7 +67,7 @@ If this variable is set to 0, no idle time is required." Changing the value requires toggling `eldoc-mode'." :type 'boolean) -(defcustom eldoc-display-truncation-message t +(defcustom eldoc-echo-area-display-truncation-message t "If non-nil, provide verbose help when a message has been truncated. If nil, truncated messages will just have \"...\" appended." :type 'boolean @@ -96,19 +96,22 @@ Note that this variable has no effect, unless If value is t, never attempt to truncate messages, even if the echo area must be resized to fit. -If value is a number (integer or floating point), it has the -semantics of `max-mini-window-height', constraining the resizing -for ElDoc purposes only. +If the value is a positive number, it is used to calculate a +number of logical lines of documentation that ElDoc is allowed to +put in the echo area. If a positive integer, the number is used +directly, while a float specifies the number of lines as a +proporting of the echo area frame's height. -Any resizing respects `max-mini-window-height'. - -If value is any non-nil symbol other than t, the part of the doc -string that represents the symbol's name may be truncated if it -will enable the rest of the doc string to fit on a single line, -without resizing the echo area. +If value is the symbol `truncate-sym-name-if-fit' t, the part of +the doc string that represents a symbol's name may be truncated +if it will enable the rest of the doc string to fit on a single +line, without resizing the echo area. If value is nil, a doc string is always truncated to fit in a -single line of display in the echo area." +single line of display in the echo area. + +Any resizing of the echo area additionally respects +`max-mini-window-height'." :type '(radio (const :tag "Always" t) (float :tag "Fraction of frame height" 0.25) (integer :tag "Number of lines" 5) @@ -117,12 +120,13 @@ single line of display in the echo area." symbol names if it will\ enable argument list to fit on one line" truncate-sym-name-if-fit))) -(defcustom eldoc-prefer-doc-buffer nil +(defcustom eldoc-echo-area-prefer-doc-buffer nil "Prefer ElDoc's documentation buffer if it is showing in some frame. -If this variable's value is t and a piece of documentation needs -to be truncated to fit in the echo area, do so if ElDoc's -documentation buffer is not already showing, since the buffer -always holds the full documentation." +If this variable's value is t, ElDoc will skip showing +documentation in the echo area if the dedicated documentation +buffer (given by `eldoc-doc-buffer') is being displayed in some +window. If the value is the symbol `maybe', then the echo area +is only skipped if the documentation doesn't fit there." :type 'boolean) (defface eldoc-highlight-function-argument @@ -237,8 +241,9 @@ expression point is on." :lighter eldoc-minor-mode-string ;; `emacs-lisp-mode' itself? (cond ((<= emacs-major-version 27) (declare-function elisp-eldoc-documentation-function "elisp-mode") - (add-function :before-until (local 'eldoc-documentation-function) - #'elisp-eldoc-documentation-function)) + (with-no-warnings + (add-function :before-until (local 'eldoc-documentation-function) + #'elisp-eldoc-documentation-function))) (t (add-hook 'eldoc-documentation-functions #'elisp-eldoc-var-docstring nil t) (add-hook 'eldoc-documentation-functions @@ -350,40 +355,26 @@ Also store it in `eldoc-last-message' and return that value." ;; for us, but do note that the last-message will be gone. (setq eldoc-last-message nil)))) -(defvar-local eldoc--last-request-state nil +;; The point of `eldoc--request-state' is not to over-request, which +;; can happen if the idle timer is restarted on execution of command +;; which is guaranteed not to change the conditions that warrant a new +;; request for documentation. +(defvar eldoc--last-request-state nil "Tuple containing information about last ElDoc request.") (defun eldoc--request-state () "Compute information to store in `eldoc--last-request-state'." (list (current-buffer) (buffer-modified-tick) (point))) (defun eldoc-display-message-p () - (eldoc--request-docs-p (eldoc--request-state))) + "Tell if ElDoc can use the echo area." + (and (eldoc-display-message-no-interference-p) + (not this-command) + (eldoc--message-command-p last-command))) + (make-obsolete 'eldoc-display-message-p "Use `eldoc-documentation-functions' instead." "eldoc-1.6.0") -(defun eldoc--request-docs-p (request-state) - "Return non-nil when it is appropriate to request docs. -REQUEST-STATE is a candidate for `eldoc--last-request-state'" - (and - ;; FIXME: The original idea behind this function is to protect the - ;; Echo area from ElDoc interference, but since that is only one of - ;; the possible outlets of ElDoc, this must soon be reworked. - (eldoc-display-message-no-interference-p) - (not (and eldoc--doc-buffer - (get-buffer-window eldoc--doc-buffer) - (equal request-state - (with-current-buffer - eldoc--doc-buffer - eldoc--last-request-state)))) - ;; If this-command is non-nil while running via an idle - ;; timer, we're still in the middle of executing a command, - ;; e.g. a query-replace where it would be annoying to - ;; overwrite the echo area. - (not this-command) - (eldoc--message-command-p last-command))) - - ;; Check various conditions about the current environment that might make ;; it undesirable to print eldoc messages right this instant. (defun eldoc-display-message-no-interference-p () @@ -416,43 +407,159 @@ about the context around point. To call the CALLBACK function, the hook function must pass it an obligatory argument DOCSTRING, a string containing the -documentation, followed by an optional list of keyword-value -pairs of the form (:KEY VALUE :KEY2 VALUE2...). KEY can be: - -* `:thing', VALUE is a short string or symbol designating what is - being reported on. The documentation display engine can elect - to remove this information depending on space constraints; - -* `:face', VALUE is a symbol designating a face to use when - displaying `:thing''s value. - -Major modes should modify this hook locally, for example: +documentation, followed by an optional list of arbitrary +keyword-value pairs of the form (:KEY VALUE :KEY2 VALUE2...). +The information contained in these pairs is understood by members +of `eldoc-display-functions', allowing the +documentation-producing backend to cooperate with specific +documentation-displaying frontends. For example, KEY can be: + +* `:thing', VALUE being a short string or symbol designating what + is being reported on. It can, for example be the name of the + function whose signature is being documented, or the name of + the variable whose docstring is being documented. + `eldoc-display-in-echo-area', a member of + `eldoc-display-functions', sometimes omits this information + depending on space constraints; + +* `:face', VALUE being a symbol designating a face which both + `eldoc-display-in-echo-area' and `eldoc-display-in-buffer' will + use when displaying `:thing''s value. + +Finally, major modes should modify this hook locally, for +example: (add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t) so that the global value (i.e. the default value of the hook) is taken into account if the major mode specific function does not return any documentation.") +(defvar eldoc-display-functions + '(eldoc-display-in-echo-area eldoc-display-in-buffer) + "Hook of functions tasked with displaying ElDoc results. +Each function is passed two arguments: DOCS and INTERACTIVE. DOCS +is a list (DOC ...) where DOC looks like (STRING :KEY VALUE :KEY2 +VALUE2 ...). STRING is a string containing the documentation's +text and the remainder of DOC is an optional list of +keyword-value pairs denoting additional properties of that +documentation. For commonly recognized properties, see +`eldoc-documentation-functions'. + +INTERACTIVE says if the request to display doc strings came +directly from the user or from ElDoc's automatic mechanisms'.") + (defvar eldoc--doc-buffer nil "Buffer displaying latest ElDoc-produced docs.") -(defun eldoc-doc-buffer (&optional interactive) - "Get latest *eldoc* help buffer. Interactively, display it." +(defvar eldoc--doc-buffer-docs nil "Documentation items in `eldoc--doc-buffer'.") + +(defun eldoc-doc-buffer () + "Display ElDoc documentation buffer. + +This holds the results of the last documentation request." + (interactive) + (unless (buffer-live-p eldoc--doc-buffer) + (user-error (format + "ElDoc buffer doesn't exist, maybe `%s' to produce one." + (substitute-command-keys "\\[eldoc]")))) + (with-current-buffer eldoc--doc-buffer + (rename-buffer (replace-regexp-in-string "^ *" "" + (buffer-name))) + (display-buffer (current-buffer)))) + +(defun eldoc--format-doc-buffer (docs) + "Ensure DOCS are displayed in an *eldoc* buffer." (interactive (list t)) - (prog1 - (if (and eldoc--doc-buffer (buffer-live-p eldoc--doc-buffer)) - eldoc--doc-buffer - (setq eldoc--doc-buffer (get-buffer-create "*eldoc*"))) - (when interactive (display-buffer eldoc--doc-buffer)))) - - -(defun eldoc--handle-docs (docs) - "Display multiple DOCS in echo area. -DOCS is a list of (STRING PLIST...). It is already sorted. -Honor most of `eldoc-echo-area-use-multiline-p'." - ;; If there's nothing to report clear the echo area, but don't erase - ;; the last *eldoc* buffer. - (if (null docs) (eldoc--message nil) + (with-current-buffer (if (buffer-live-p eldoc--doc-buffer) + eldoc--doc-buffer + (setq eldoc--doc-buffer + (get-buffer-create " *eldoc*"))) + (unless (eq docs eldoc--doc-buffer-docs) + (setq-local eldoc--doc-buffer-docs docs) + (let ((inhibit-read-only t) + (things-reported-on)) + (erase-buffer) (setq buffer-read-only t) + (local-set-key "q" 'quit-window) + (cl-loop for (docs . rest) on docs + for (this-doc . plist) = docs + for thing = (plist-get plist :thing) + when thing do + (cl-pushnew thing things-reported-on) + (setq this-doc + (concat + (propertize (format "%s" thing) + 'face (plist-get plist :face)) + ": " + this-doc)) + do (insert this-doc) + when rest do (insert "\n") + finally (goto-char (point-min))) + ;; Rename the buffer, taking into account whether it was + ;; hidden or not + (rename-buffer (format "%s*eldoc%s*" + (if (string-match "^ " (buffer-name)) " " "") + (if things-reported-on + (format " for %s" + (mapconcat + (lambda (s) (format "%s" s)) + things-reported-on + ", ")) + "")))))) + eldoc--doc-buffer) + +(defun eldoc--echo-area-substring (available) + "Given AVAILABLE lines, get buffer substring to display in echo area. +Helper for `eldoc-display-in-echo-area'." + (let ((start (prog1 (progn + (goto-char (point-min)) + (skip-chars-forward " \t\n") + (point)) + (goto-char (line-end-position available)) + (skip-chars-backward " \t\n"))) + (truncated (save-excursion + (skip-chars-forward " \t\n") + (not (eobp))))) + (cond ((eldoc--echo-area-prefer-doc-buffer-p truncated) + nil) + ((and truncated + (> available 1) + eldoc-echo-area-display-truncation-message) + (goto-char (line-end-position 0)) + (concat (buffer-substring start (point)) + (format + "\n(Documentation truncated. Use `%s' to see rest)" + (substitute-command-keys "\\[eldoc-doc-buffer]")))) + (t + (buffer-substring start (point)))))) + +(defun eldoc--echo-area-prefer-doc-buffer-p (truncatedp) + "Tell if display in the echo area should be skipped. +Helper for `eldoc-display-in-echo-area'. If TRUNCATEDP the +documentation to potentially appear in the echo are is truncated." + (and (or (eq eldoc-echo-area-prefer-doc-buffer t) + (and truncatedp + (eq eldoc-echo-area-prefer-doc-buffer + 'maybe))) + (get-buffer-window eldoc--doc-buffer))) + +(defun eldoc-display-in-echo-area (docs _interactive) + "Display DOCS in echo area. +Honor `eldoc-echo-area-use-multiline-p' and +`eldoc-echo-area-prefer-doc-buffer'." + (cond + (;; Check if he wave permission to mess with echo area at all. For + ;; example, if this-command is non-nil while running via an idle + ;; timer, we're still in the middle of executing a command, e.g. a + ;; query-replace where it would be annoying to overwrite the echo + ;; area. + (or + (not (eldoc-display-message-no-interference-p)) + this-command + (not (eldoc--message-command-p last-command)))) + (;; If we do but nothing to report, clear the echo area. + (null docs) + (eldoc--message nil)) + (t + ;; Otherwise, establish some parameters. (let* - ;; Otherwise, establish some parameters. ((width (1- (window-width (minibuffer-window)))) (val (if (and (symbolp eldoc-echo-area-use-multiline-p) eldoc-echo-area-use-multiline-p) @@ -461,44 +568,13 @@ Honor most of `eldoc-echo-area-use-multiline-p'." (available (cl-typecase val (float (truncate (* (frame-height) val))) (integer val) - (t 1))) - (things-reported-on) - (request eldoc--last-request-state) + (t 'just-one-line))) single-doc single-doc-sym) - ;; Then, compose the contents of the `*eldoc*' buffer. - (with-current-buffer (eldoc-doc-buffer) - ;; Set doc-buffer's `eldoc--last-request-state', too - (setq eldoc--last-request-state request) - (let ((inhibit-read-only t)) - (erase-buffer) (setq buffer-read-only t) - (local-set-key "q" 'quit-window) - (cl-loop for (docs . rest) on docs - for (this-doc . plist) = docs - for thing = (plist-get plist :thing) - when thing do - (cl-pushnew thing things-reported-on) - (setq this-doc - (concat - (propertize (format "%s" thing) - 'face (plist-get plist :face)) - ": " - this-doc)) - do (insert this-doc) - when rest do (insert "\n"))) - ;; Rename the buffer. - (when things-reported-on - (rename-buffer (format "*eldoc for %s*" - (mapconcat (lambda (s) (format "%s" s)) - things-reported-on - ", "))))) - ;; Finally, output to the echo area. I'm pretty sure nicer - ;; strategies can be used here, probably by splitting this - ;; function into some `eldoc-display-functions' special hook. (let ((echo-area-message (cond - (;; We handle the `truncate-sym-name-if-fit' special - ;; case first, by checking if for a lot of special - ;; conditions. + (;; To output to the echo area, we handle the + ;; `truncate-sym-name-if-fit' special case first, by + ;; checking for a lot of special conditions. (and (eq 'truncate-sym-name-if-fit eldoc-echo-area-use-multiline-p) (null (cdr docs)) @@ -509,45 +585,32 @@ Honor most of `eldoc-echo-area-use-multiline-p'." (not (string-match "\n" single-doc)) (> (+ (length single-doc) (length single-doc-sym) 2) width)) single-doc) - ((> available 1) - ;; The message takes one extra line, so if we don't - ;; display that, we have one extra line to use. - (unless eldoc-display-truncation-message - (setq available (1+ available))) - (with-current-buffer (eldoc-doc-buffer) - (cl-loop - initially - (goto-char (point-min)) - (goto-char (line-end-position (1+ available))) - for truncated = nil then t - for needed - = (let ((truncate-lines message-truncate-lines)) - (count-screen-lines (point-min) (point) t - (minibuffer-window))) - while (> needed (if truncated (1- available) available)) - do (goto-char (line-end-position (if truncated 0 -1))) - (while (and (not (bobp)) (bolp)) (goto-char (line-end-position 0))) - finally - (unless (and truncated - eldoc-prefer-doc-buffer - (get-buffer-window eldoc--doc-buffer)) - (cl-return - (concat - (buffer-substring (point-min) (point)) - (and - truncated - (if eldoc-display-truncation-message - (format - "\n(Documentation truncated. Use `%s' to see rest)" - (substitute-command-keys "\\[eldoc-doc-buffer]")) - "...")))))))) - ((= available 1) - ;; Truncate "brutally." ; FIXME: use `eldoc-prefer-doc-buffer' too? - (with-current-buffer (eldoc-doc-buffer) - (truncate-string-to-width - (buffer-substring (goto-char (point-min)) (line-end-position 1)) width)))))) + ((and (numberp available) + (cl-plusp available)) + ;; Else, given a positive number of logical lines, we + ;; format the *eldoc* buffer, using as most of its + ;; contents as we know will fit. + (with-current-buffer (eldoc--format-doc-buffer docs) + (eldoc--echo-area-substring available))) + (t ;; this is the "truncate brutally" situation + (let ((string + (with-current-buffer (eldoc--format-doc-buffer docs) + (buffer-substring (goto-char (point-min)) + (line-end-position 1))))) + (if (> (length string) width) ; truncation to happen + (unless (eldoc--echo-area-prefer-doc-buffer-p t) + (truncate-string-to-width string width)) + (unless (eldoc--echo-area-prefer-doc-buffer-p nil) + string))))))) (when echo-area-message - (eldoc--message echo-area-message)))))) + (eldoc--message echo-area-message))))))) + +(defun eldoc-display-in-buffer (docs interactive) + "Display DOCS in a dedicated buffer. +If INTERACTIVE is t, also display the buffer." + (eldoc--format-doc-buffer docs) + (when interactive + (eldoc-doc-buffer))) (defun eldoc-documentation-default () "Show first doc string for item at point. @@ -709,19 +772,29 @@ have the following values: strings so far, as soon as possible." (funcall eldoc--make-callback method)) -(defun eldoc--invoke-strategy () +(defun eldoc--invoke-strategy (interactive) "Invoke `eldoc-documentation-strategy' function. +If INTERACTIVE is non-nil, the request came directly from a user +command, otherwise it came from ElDoc's idle +timer, `eldoc-timer'. + That function's job is to run the `eldoc-documentation-functions' special hook, using the `run-hook' family of functions. ElDoc's built-in strategy functions play along with the -`eldoc--make-callback' protocol, using it to produce callback to -feed to the functgions of `eldoc-documentation-functions'. - -Other third-party strategy functions do not use -`eldoc--make-callback'. They must find some alternate way to -produce callbacks to feed to `eldoc-documentation-function' and -should endeavour to display the docstrings eventually produced." +`eldoc--make-callback' protocol, using it to produce a callback +argument to feed the functions that the user places in +`eldoc-documentation-functions'. Whenever the strategy +determines it has information to display to the user, this +function passes responsibility to the functions in +`eldoc-display-functions'. + +Other third-party values of `eldoc-documentation-strategy' should +not use `eldoc--make-callback'. They must find some alternate +way to produce callbacks to feed to +`eldoc-documentation-function' and should endeavour to display +the docstrings eventually produced, using +`eldoc-display-functions'." (let* (;; How many callbacks have been created by the strategy ;; function and passed to elements of ;; `eldoc-documentation-functions'. @@ -739,11 +812,12 @@ should endeavour to display the docstrings eventually produced." (push (cons pos (cons string plist)) docs-registered))) (display-doc () - (eldoc--handle-docs - (mapcar #'cdr - (setq docs-registered - (sort docs-registered - (lambda (a b) (< (car a) (car b)))))))) + (run-hook-with-args + 'eldoc-display-functions (mapcar #'cdr + (setq docs-registered + (sort docs-registered + (lambda (a b) (< (car a) (car b)))))) + interactive)) (make-callback (method) (let ((pos (prog1 howmany (cl-incf howmany)))) @@ -786,22 +860,23 @@ should endeavour to display the docstrings eventually produced." (defun eldoc-print-current-symbol-info (&optional interactive) "Document thing at point." (interactive '(t)) - (let ((token (eldoc--request-state))) + (let (token) (cond (interactive - (eldoc--invoke-strategy)) - ((not (eldoc--request-docs-p token)) - ;; Erase the last message if we won't display a new one. - (when eldoc-last-message - (eldoc--message nil))) - (t + (eldoc--invoke-strategy t)) + ((not (equal (setq token (eldoc--request-state)) + eldoc--last-request-state)) (let ((non-essential t)) (setq eldoc--last-request-state token) ;; Only keep looking for the info as long as the user hasn't ;; requested our attention. This also locally disables ;; inhibit-quit. (while-no-input - (eldoc--invoke-strategy))))))) + (eldoc--invoke-strategy nil))))))) + +;; This section only affects ElDoc output to the echo area, as in +;; `eldoc-display-in-echo-area'. +;; ;; When point is in a sexp, the function args are not reprinted in the echo ;; area after every possible interactive command because some of them print ;; their own messages in the echo area; the eldoc functions would instantly @@ -833,7 +908,6 @@ should endeavour to display the docstrings eventually produced." (apply #'eldoc-remove-command (all-completions name eldoc-message-commands)))) - ;; Prime the command list. (eldoc-add-command-completions "back-to-indentation" diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index ebb27e8a62c..baa04f2c6af 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -1305,7 +1305,8 @@ EXPECTEDP specifies whether the result was expected." "Pretty-print OBJECT, indenting it to the current column of point. Ensures a final newline is inserted." (let ((begin (point)) - (pp-escape-newlines nil)) + (pp-escape-newlines nil) + (print-escape-control-characters t)) (pp object (current-buffer)) (unless (bolp) (insert "\n")) (save-excursion diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index 7ae6d53a21b..dd9cbd5d55a 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -25,25 +25,24 @@ ;;; Code: (require 'seq) +(require 'text-property-search) (eval-when-compile (require 'cl-lib)) (defgroup shortdoc nil "Short documentation." :group 'lisp) -(defface shortdoc-section +(defface shortdoc-separator '((((class color) (background dark)) - :inherit variable-pitch :background "#303030" :extend t) + :height 0.1 :background "#505050" :extend t) (((class color) (background light)) - :inherit variable-pitch :background "#f0f0f0" :extend t)) - "Face used for a section.") + :height 0.1 :background "#a0a0a0" :extend t) + (t :height 0.1 :inverse-video t :extend t)) + "Face used to separate sections.") -(defface shortdoc-example - '((((class color) (background dark)) - :background "#202020" :extend t) - (((class color) (background light)) - :background "#e8e8e8" :extend t)) - "Face used for examples.") +(defface shortdoc-section + '((t :inherit variable-pitch)) + "Face used for a section.") (defvar shortdoc--groups nil) @@ -78,6 +77,45 @@ There can be any number of :example/:result elements." shortdoc--groups)) (push (cons ',group ',functions) shortdoc--groups))) +(define-short-documentation-group alist + "Alist Basics" + (assoc + :eval (assoc 'foo '((foo . bar) (zot . baz)))) + (rassoc + :eval (rassoc 'bar '((foo . bar) (zot . baz)))) + (assq + :eval (assq 'foo '((foo . bar) (zot . baz)))) + (rassq + :eval (rassq 'bar '((foo . bar) (zot . baz)))) + (assoc-string + :eval (assoc-string "foo" '(("foo" . "bar") ("zot" "baz")))) + "Manipulating Alists" + (assoc-delete-all + :eval (assoc-delete-all "foo" '(("foo" . "bar") ("zot" . "baz")) #'equal)) + (assq-delete-all + :eval (assq-delete-all 'foo '((foo . bar) (zot . baz)))) + (rassq-delete-all + :eval (rassq-delete-all 'bar '((foo . bar) (zot . baz)))) + (alist-get + :eval (let ((foo '((bar . baz)))) + (setf (alist-get 'bar foo) 'zot) + foo)) + "Misc" + (assoc-default + :eval (assoc-default "foobar" '(("foo" . baz)) #'string-match)) + (copy-alist + :eval (let* ((old '((foo . bar))) + (new (copy-alist old))) + (eq old new))) + ;; FIXME: Outputs "\.rose" for the symbol `.rose'. + ;; (let-alist + ;; :eval (let ((colors '((rose . red) + ;; (lily . white)))) + ;; (let-alist colors + ;; (if (eq .rose 'red) + ;; .lily)))) + ) + (define-short-documentation-group string "Making Strings" (make-string @@ -380,6 +418,37 @@ There can be any number of :example/:result elements." :no-eval (set-file-acl "/tmp/foo" "group::rxx") :eg-result t)) +(define-short-documentation-group hash-table + "Hash Table Basics" + (make-hash-table + :no-eval (make-hash-table) + :result-string "#s(hash-table ...)") + (puthash + :no-eval (puthash 'key "value" table)) + (gethash + :no-eval (gethash 'key table) + :eg-result "value") + (remhash + :no-eval (remhash 'key table) + :result nil) + (clrhash + :no-eval (clrhash table) + :result-string "#s(hash-table ...)") + (maphash + :no-eval (maphash (lambda (key value) (message value)) table) + :result nil) + "Other Hash Table Functions" + (hash-table-p + :eval (hash-table-p 123)) + (copy-hash-table + :no-eval (copy-hash-table table) + :result-string "#s(hash-table ...)") + (hash-table-count + :no-eval (hash-table-count table) + :eg-result 15) + (hash-table-size + :no-eval (hash-table-size table) + :eg-result 65)) (define-short-documentation-group list "Making Lists" @@ -557,15 +626,6 @@ There can be any number of :example/:result elements." :eval (replace-regexp-in-string "[a-z]+" "_" "*foo*")) (string-match-p :eval (string-match-p "^[fo]+" "foobar")) - (match-string - :eval (and (string-match "^\\([fo]+\\)b" "foobar") - (match-string 0 "foobar"))) - (match-beginning - :no-eval (match-beginning 1) - :eg-result 0) - (match-end - :no-eval (match-end 1) - :eg-result 3) "Looking in Buffers" (re-search-forward :no-eval (re-search-forward "^foo$" nil t) @@ -576,6 +636,25 @@ There can be any number of :example/:result elements." (looking-at-p :no-eval (looking-at "f[0-9]") :eg-result t) + "Match Data" + (match-string + :eval (and (string-match "^\\([fo]+\\)b" "foobar") + (match-string 0 "foobar"))) + (match-beginning + :no-eval (match-beginning 1) + :eg-result 0) + (match-end + :no-eval (match-end 1) + :eg-result 3) + (save-match-data + :no-eval (save-match-data ...)) + "Replacing Match" + (replace-match + :no-eval (replace-match "new") + :eg-result nil) + (match-substitute-replacement + :no-eval (match-substitute-replacement "new") + :eg-result "new") "Utilities" (regexp-quote :eval (regexp-quote "foo.*bar")) @@ -584,7 +663,28 @@ There can be any number of :example/:result elements." (regexp-opt-depth :eval (regexp-opt-depth "\\(a\\(b\\)\\)")) (regexp-opt-charset - :eval (regexp-opt-charset '(?a ?b ?c ?d ?e)))) + :eval (regexp-opt-charset '(?a ?b ?c ?d ?e))) + "The `rx' Structured Regexp Notation" + (rx + :eval (rx "IP=" (+ digit) (= 3 "." (+ digit)))) + (rx-to-string + :eval (rx-to-string '(| "foo" "bar"))) + (rx-define + :no-eval "(and (rx-define haskell-comment (seq \"--\" (zero-or-more nonl))) + (rx haskell-comment))" + :result "--.*") + (rx-let + :eval "(rx-let ((comma-separated (item) (seq item (0+ \",\" item))) + (number (1+ digit)) + (numbers (comma-separated number))) + (rx \"(\" numbers \")\"))" + :result "([[:digit:]]+\\(?:,[[:digit:]]+\\)*)") + (rx-let-eval + :eval "(rx-let-eval + '((ponder (x) (seq \"Where have all the \" x \" gone?\"))) + (rx-to-string + '(ponder (or \"flowers\" \"cars\" \"socks\"))))" + :result "\\(?:Where have all the \\(?:\\(?:car\\|flower\\|sock\\)s\\) gone\\?\\)")) (define-short-documentation-group sequence "Sequence Predicates" @@ -963,19 +1063,27 @@ There can be any number of :example/:result elements." (unless (assq group shortdoc--groups) (error "No such documentation group %s" group)) (pop-to-buffer (format "*Shortdoc %s*" group)) - (let ((inhibit-read-only t)) + (let ((inhibit-read-only t) + (prev nil)) (erase-buffer) - (special-mode) + (shortdoc-mode) (button-mode) (mapc (lambda (data) (cond ((stringp data) + (setq prev nil) + (unless (bobp) + (insert "\n")) (insert (propertize - (concat data "\n\n") - 'face '(variable-pitch (:height 1.3 :weight bold))))) + (concat (substitute-command-keys data) "\n\n") + 'face '(variable-pitch (:height 1.3 :weight bold)) + 'shortdoc-section t))) ;; There may be functions not yet defined in the data. ((fboundp (car data)) + (when prev + (insert (propertize "\n" 'face 'shortdoc-separator))) + (setq prev t) (shortdoc--display-function data)))) (cdr (assq group shortdoc--groups)))) (goto-char (point-min))) @@ -985,7 +1093,8 @@ There can be any number of :example/:result elements." (start-section (point)) arglist-start) ;; Function calling convention. - (insert "(") + (insert (propertize "(" + 'shortdoc-function t)) (if (plist-get data :no-manual) (insert (symbol-name function)) (insert-text-button @@ -1001,8 +1110,7 @@ There can be any number of :example/:result elements." (car (split-string (documentation function) "\n")))) (insert "\n") (add-face-text-property start-section (point) 'shortdoc-section t) - (let ((start (point)) - (print-escape-newlines t) + (let ((print-escape-newlines t) (double-arrow (if (char-displayable-p ?⇒) "⇒" "=>")) @@ -1057,9 +1165,7 @@ There can be any number of :example/:result elements." (:eg-result-string (insert " eg. " double-arrow " ") (princ value (current-buffer)) - (insert "\n")))) - (put-text-property start (point) 'face 'shortdoc-example)) - (insert "\n") + (insert "\n"))))) ;; Insert the arglist after doing the evals, in case that's pulled ;; in the function definition. (save-excursion @@ -1098,6 +1204,51 @@ Example: (setq slist (cdr slist))) (setcdr slist (cons elem (cdr slist)))))) +(defvar shortdoc-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "n") 'shortdoc-next) + (define-key map (kbd "p") 'shortdoc-previous) + (define-key map (kbd "C-c C-n") 'shortdoc-next-section) + (define-key map (kbd "C-c C-p") 'shortdoc-previous-section) + map) + "Keymap for `shortdoc-mode'") + +(define-derived-mode shortdoc-mode special-mode "shortdoc" + "Mode for shortdoc.") + +(defmacro shortdoc--goto-section (arg sym &optional reverse) + `(progn + (unless (natnump ,arg) + (setq ,arg 1)) + (while (< 0 ,arg) + (,(if reverse + 'text-property-search-backward + 'text-property-search-forward) + ,sym t) + (setq ,arg (1- ,arg))))) + +(defun shortdoc-next (&optional arg) + "Move cursor to next function." + (interactive "p") + (shortdoc--goto-section arg 'shortdoc-function)) + +(defun shortdoc-previous (&optional arg) + "Move cursor to previous function." + (interactive "p") + (shortdoc--goto-section arg 'shortdoc-function t) + (backward-char 1)) + +(defun shortdoc-next-section (&optional arg) + "Move cursor to next section." + (interactive "p") + (shortdoc--goto-section arg 'shortdoc-section)) + +(defun shortdoc-previous-section (&optional arg) + "Move cursor to previous section." + (interactive "p") + (shortdoc--goto-section arg 'shortdoc-section t) + (forward-line -2)) + (provide 'shortdoc) ;;; shortdoc.el ends here diff --git a/lisp/emacs-lisp/text-property-search.el b/lisp/emacs-lisp/text-property-search.el index 61bd98d3cfe..d7dc7da7c18 100644 --- a/lisp/emacs-lisp/text-property-search.el +++ b/lisp/emacs-lisp/text-property-search.el @@ -34,11 +34,11 @@ "Search for the next region of text whose PROPERTY matches VALUE. If not found, return nil and don't move point. -If found, move point to end of the region and return a `prop-match' -object describing the match. To access the details of the match, -use `prop-match-beginning' and `prop-match-end' for the buffer -positions that limit the region, and `prop-match-value' for the -value of PROPERTY in the region. +If found, move point to the start of the region and return a +`prop-match' object describing the match. To access the details +of the match, use `prop-match-beginning' and `prop-match-end' for +the buffer positions that limit the region, and +`prop-match-value' for the value of PROPERTY in the region. PREDICATE is used to decide whether a value of PROPERTY should be considered as matching VALUE. @@ -125,7 +125,7 @@ that matches VALUE." "Search for the previous region of text whose PROPERTY matches VALUE. Like `text-property-search-forward', which see, but searches backward, -and if a matching region is found, moves point to its beginning." +and if a matching region is found, place point at its end." (interactive (list (let ((string (completing-read "Search for property: " obarray))) diff --git a/lisp/emacs-lisp/timer-list.el b/lisp/emacs-lisp/timer-list.el index 00d09696d2a..4bda9acebf7 100644 --- a/lisp/emacs-lisp/timer-list.el +++ b/lisp/emacs-lisp/timer-list.el @@ -49,23 +49,25 @@ (let ((time (list (aref timer 1) (aref timer 2) (aref timer 3)))) - (format "%10.2f" - (float-time - (if (aref timer 7) - time - (time-subtract time nil))))) - 'help-echo "Time in sec till next invocation") + (format "%12s" + (format-seconds "%dd %hh %mm %z%,1ss" + (float-time + (if (aref timer 7) + time + (time-subtract time nil)))))) + 'help-echo "Time until next invocation") ;; Repeat. - ,(propertize - (let ((repeat (aref timer 4))) - (cond - ((numberp repeat) - (format "%8.1f" repeat)) - ((null repeat) - " -") - (t - (format "%8s" repeat)))) - 'help-echo "Symbol: repeat; number: repeat interval in sec") + ,(let ((repeat (aref timer 4))) + (cond + ((numberp repeat) + (propertize + (format "%12s" (format-seconds + "%dd %hh %mm %z%,1ss" repeat)) + 'help-echo "Repeat interval")) + ((null repeat) + (propertize " -" 'help-echo "Runs once")) + (t + (format "%12s" repeat)))) ;; Function. ,(propertize (let ((cl-print-compiled 'static) @@ -93,8 +95,8 @@ (setq-local revert-buffer-function #'list-timers) (setq tabulated-list-format '[("Idle" 6 timer-list--idle-predicate) - (" Next" 12 timer-list--next-predicate) - (" Repeat" 11 timer-list--repeat-predicate) + (" Next" 12 timer-list--next-predicate) + (" Repeat" 12 timer-list--repeat-predicate) ("Function" 10 timer-list--function-predicate)])) (defun timer-list--idle-predicate (A B) diff --git a/lisp/emacs-lisp/unsafep.el b/lisp/emacs-lisp/unsafep.el index e7077140e54..c4db86a0db3 100644 --- a/lisp/emacs-lisp/unsafep.el +++ b/lisp/emacs-lisp/unsafep.el @@ -91,17 +91,41 @@ in the parse.") (put 'unsafep-vars 'risky-local-variable t) -;;Other safe functions +;; Other safe forms. +;; +;; A function, macro or special form may be put here only if all of +;; the following statements are true: +;; +;; * It is not already marked `pure' or `side-effect-free', or handled +;; explicitly by `unsafep'. +;; +;; * It is not inherently unsafe; eg, would allow the execution of +;; arbitrary code, interact with the file system, network or other +;; processes, or otherwise exfiltrate information from the running +;; Emacs process or manipulate the user's environment. +;; +;; * It does not have side-effects that can make other code behave in +;; unsafe and/or unexpected ways; eg, set variables, mutate data, or +;; change control flow. +;; Any side effect must be innocuous; altering the match data is +;; explicitly permitted. +;; +;; * It does not allow Emacs to behave deceptively to the user; eg, +;; display arbitrary messages. +;; +;; * It does not present a potentially large attack surface; eg, +;; play arbitrary audio files. + (dolist (x '(;;Special forms - and catch if or prog1 prog2 progn while unwind-protect + and if or prog1 prog2 progn while unwind-protect ;;Safe subrs that have some side-effects - ding error random signal sleep-for string-match throw + ding random sleep-for string-match ;;Defsubst functions from subr.el caar cadr cdar cddr ;;Macros from subr.el save-match-data unless when ;;Functions from subr.el that have side effects - split-string replace-regexp-in-string play-sound-file)) + split-string)) (put x 'safe-function t)) ;;;###autoload |