summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
authorAndrea Corallo <akrl@sdf.org>2020-10-31 15:00:00 +0000
committerAndrea Corallo <akrl@sdf.org>2020-10-31 15:00:00 +0000
commitf7f5d59ab4c4cc1a7db46d7f1d462655254e1a87 (patch)
tree33b8af817af32d9414fe3aa08f22c4ce2aa4dc38 /lisp/emacs-lisp
parentfd9e9308d27138a16e2e93417bd7ad4448fea40a (diff)
parent283b8d274bd54192b3876ce8bf2930a096391839 (diff)
downloademacs-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.el174
-rw-r--r--lisp/emacs-lisp/byte-opt.el14
-rw-r--r--lisp/emacs-lisp/bytecomp.el3
-rw-r--r--lisp/emacs-lisp/checkdoc.el2
-rw-r--r--lisp/emacs-lisp/copyright.el9
-rw-r--r--lisp/emacs-lisp/easy-mmode.el103
-rw-r--r--lisp/emacs-lisp/edebug.el17
-rw-r--r--lisp/emacs-lisp/eldoc.el406
-rw-r--r--lisp/emacs-lisp/ert.el3
-rw-r--r--lisp/emacs-lisp/shortdoc.el211
-rw-r--r--lisp/emacs-lisp/text-property-search.el12
-rw-r--r--lisp/emacs-lisp/timer-list.el38
-rw-r--r--lisp/emacs-lisp/unsafep.el32
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