summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/advice.el4
-rw-r--r--lisp/emacs-lisp/autoload.el453
-rw-r--r--lisp/emacs-lisp/avl-tree.el12
-rw-r--r--lisp/emacs-lisp/backquote.el10
-rw-r--r--lisp/emacs-lisp/benchmark.el6
-rw-r--r--lisp/emacs-lisp/byte-opt.el73
-rw-r--r--lisp/emacs-lisp/byte-run.el7
-rw-r--r--lisp/emacs-lisp/bytecomp.el593
-rw-r--r--lisp/emacs-lisp/cconv.el109
-rw-r--r--lisp/emacs-lisp/chart.el73
-rw-r--r--lisp/emacs-lisp/check-declare.el142
-rw-r--r--lisp/emacs-lisp/checkdoc.el112
-rw-r--r--lisp/emacs-lisp/cl-extra.el125
-rw-r--r--lisp/emacs-lisp/cl-generic.el169
-rw-r--r--lisp/emacs-lisp/cl-indent.el36
-rw-r--r--lisp/emacs-lisp/cl-lib.el183
-rw-r--r--lisp/emacs-lisp/cl-macs.el199
-rw-r--r--lisp/emacs-lisp/cl-preloaded.el27
-rw-r--r--lisp/emacs-lisp/cl-print.el244
-rw-r--r--lisp/emacs-lisp/cl-seq.el99
-rw-r--r--lisp/emacs-lisp/cl.el27
-rw-r--r--lisp/emacs-lisp/debug.el103
-rw-r--r--lisp/emacs-lisp/derived.el28
-rw-r--r--lisp/emacs-lisp/disass.el18
-rw-r--r--lisp/emacs-lisp/edebug.el234
-rw-r--r--lisp/emacs-lisp/eieio-base.el3
-rw-r--r--lisp/emacs-lisp/eieio-compat.el8
-rw-r--r--lisp/emacs-lisp/eieio-core.el159
-rw-r--r--lisp/emacs-lisp/eieio-custom.el2
-rw-r--r--lisp/emacs-lisp/eieio-datadebug.el2
-rw-r--r--lisp/emacs-lisp/eieio-opt.el2
-rw-r--r--lisp/emacs-lisp/eieio.el107
-rw-r--r--lisp/emacs-lisp/eldoc.el41
-rw-r--r--lisp/emacs-lisp/elint.el18
-rw-r--r--lisp/emacs-lisp/ert-x.el26
-rw-r--r--lisp/emacs-lisp/ert.el144
-rw-r--r--lisp/emacs-lisp/find-func.el117
-rw-r--r--lisp/emacs-lisp/generator.el1
-rw-r--r--lisp/emacs-lisp/let-alist.el16
-rw-r--r--lisp/emacs-lisp/lisp-mode.el320
-rw-r--r--lisp/emacs-lisp/lisp.el136
-rw-r--r--lisp/emacs-lisp/macroexp.el4
-rw-r--r--lisp/emacs-lisp/map-ynp.el3
-rw-r--r--lisp/emacs-lisp/map.el63
-rw-r--r--lisp/emacs-lisp/nadvice.el18
-rw-r--r--lisp/emacs-lisp/package.el136
-rw-r--r--lisp/emacs-lisp/pcase.el22
-rw-r--r--lisp/emacs-lisp/pp.el51
-rw-r--r--lisp/emacs-lisp/radix-tree.el246
-rw-r--r--lisp/emacs-lisp/re-builder.el8
-rw-r--r--lisp/emacs-lisp/regexp-opt.el4
-rw-r--r--lisp/emacs-lisp/ring.el9
-rw-r--r--lisp/emacs-lisp/rx.el2
-rw-r--r--lisp/emacs-lisp/seq.el89
-rw-r--r--lisp/emacs-lisp/smie.el4
-rw-r--r--lisp/emacs-lisp/subr-x.el246
-rw-r--r--lisp/emacs-lisp/syntax.el9
-rw-r--r--lisp/emacs-lisp/tabulated-list.el76
-rw-r--r--lisp/emacs-lisp/testcover.el1
-rw-r--r--lisp/emacs-lisp/thunk.el2
-rw-r--r--lisp/emacs-lisp/timer-list.el112
-rw-r--r--lisp/emacs-lisp/timer.el2
62 files changed, 3610 insertions, 1685 deletions
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el
index 6c49928aee8..3342bea209a 100644
--- a/lisp/emacs-lisp/advice.el
+++ b/lisp/emacs-lisp/advice.el
@@ -1832,7 +1832,7 @@ Redefining advices affect the construction of an advised definition."
;; @@ Interactive input functions:
;; ===============================
-(declare-function 'function-called-at-point "help")
+(declare-function function-called-at-point "help")
(defun ad-read-advised-function (&optional prompt predicate default)
"Read name of advised function with completion from the minibuffer.
@@ -2830,7 +2830,7 @@ advised definition from scratch."
(ad-get-cache-id function))))
(ad-set-advice-info function old-advice-info)
(advice-remove function advicefunname)
- (fset advicefunname old-advice)
+ (if advicefunname (fset advicefunname old-advice))
(if old-advice (advice-add function :around advicefunname)))))
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el
index 0f5c04b0ae4..8fe94013700 100644
--- a/lisp/emacs-lisp/autoload.el
+++ b/lisp/emacs-lisp/autoload.el
@@ -87,6 +87,29 @@ that text will be copied verbatim to `generated-autoload-file'.")
(defconst generate-autoload-section-continuation ";;;;;; "
"String to add on each continuation of the section header form.")
+;; In some ways it would be nicer to use a value that is recognizably
+;; not a time-value, eg t, but that can cause issues if an older Emacs
+;; that does not expect non-time-values loads the file.
+(defconst autoload--non-timestamp '(0 0 0 0)
+ "Value to insert when `autoload-timestamps' is nil.")
+
+(defvar autoload-timestamps nil ; experimental, see bug#22213
+ "Non-nil means insert a timestamp for each input file into the output.
+We use these in incremental updates of the output file to decide
+if we need to rescan an input file. If you set this to nil,
+then we use the timestamp of the output file instead. As a result:
+ - for fixed inputs, the output will be the same every time
+ - incremental updates of the output file might not be correct if:
+ i) the timestamp of the output file cannot be trusted (at least
+ relative to that of the input files)
+ ii) any of the input files can be modified during the time it takes
+ to create the output
+ iii) only a subset of the input files are scanned
+ These issues are unlikely to happen in practice, and would arguably
+ represent bugs in the build system. Item iii) will happen if you
+ use a command like `update-file-autoloads', though, since it only
+ checks a single input file.")
+
(defvar autoload-modified-buffers) ;Dynamically scoped var.
(defun make-autoload (form file &optional expansion)
@@ -141,7 +164,8 @@ expression, in which case we want to handle forms differently."
((and (memq car '(easy-mmode-define-global-mode define-global-minor-mode
define-globalized-minor-mode defun defmacro
easy-mmode-define-minor-mode define-minor-mode
- define-inline cl-defun cl-defmacro))
+ define-inline cl-defun cl-defmacro cl-defgeneric
+ pcase-defmacro))
(macrop car)
(setq expand (let ((load-file-name file)) (macroexpand form)))
(memq (car expand) '(progn prog1 defalias)))
@@ -160,10 +184,12 @@ expression, in which case we want to handle forms differently."
(args (pcase car
((or `defun `defmacro
`defun* `defmacro* `cl-defun `cl-defmacro
- `define-overloadable-function) (nth 2 form))
+ `define-overloadable-function)
+ (nth 2 form))
(`define-skeleton '(&optional str arg))
((or `define-generic-mode `define-derived-mode
- `define-compilation-mode) nil)
+ `define-compilation-mode)
+ nil)
(_ t)))
(body (nthcdr (or (function-get car 'doc-string-elt) 3) form))
(doc (if (stringp (car body)) (pop body))))
@@ -179,7 +205,8 @@ expression, in which case we want to handle forms differently."
define-global-minor-mode
define-globalized-minor-mode
easy-mmode-define-minor-mode
- define-minor-mode)) t)
+ define-minor-mode))
+ t)
(eq (car-safe (car body)) 'interactive))
,(if macrop ''macro nil))))
@@ -228,17 +255,22 @@ expression, in which case we want to handle forms differently."
;; Those properties are now set in lisp-mode.el.
(defun autoload-find-generated-file ()
- "Visit the autoload file for the current buffer, and return its buffer.
-If a buffer is visiting the desired autoload file, return it."
+ "Visit the autoload file for the current buffer, and return its buffer."
(let ((enable-local-variables :safe)
- (enable-local-eval nil))
+ (enable-local-eval nil)
+ (delay-mode-hooks t)
+ (file (autoload-generated-file)))
;; We used to use `raw-text' to read this file, but this causes
;; problems when the file contains non-ASCII characters.
- (let ((delay-mode-hooks t))
- (find-file-noselect
- (autoload-ensure-default-file (autoload-generated-file))))))
+ (with-current-buffer (find-file-noselect
+ (autoload-ensure-file-writeable file))
+ (if (zerop (buffer-size)) (insert (autoload-rubric file nil t)))
+ (current-buffer))))
(defun autoload-generated-file ()
+ "Return `generated-autoload-file' as an absolute name.
+If local to the current buffer, expand using the default directory;
+otherwise, using `source-directory'/lisp."
(expand-file-name generated-autoload-file
;; File-local settings of generated-autoload-file should
;; be interpreted relative to the file's location,
@@ -277,7 +309,7 @@ The variable `autoload-print-form-outbuf' specifies the buffer to
put the output in."
(cond
;; If the form is a sequence, recurse.
- ((eq (car form) 'progn) (mapcar 'autoload-print-form (cdr form)))
+ ((eq (car form) 'progn) (mapcar #'autoload-print-form (cdr form)))
;; Symbols at the toplevel are meaningless.
((symbolp form) nil)
(t
@@ -323,24 +355,28 @@ put the output in."
(defun autoload-rubric (file &optional type feature)
"Return a string giving the appropriate autoload rubric for FILE.
TYPE (default \"autoloads\") is a string stating the type of
-information contained in FILE. If FEATURE is non-nil, FILE
-will provide a feature. FEATURE may be a string naming the
-feature, otherwise it will be based on FILE's name.
-
-At present, a feature is in fact always provided, but this should
-not be relied upon."
- (let ((basename (file-name-nondirectory file)))
+information contained in FILE. TYPE \"package\" acts like the default,
+but adds an extra line to the output to modify `load-path'.
+
+If FEATURE is non-nil, FILE will provide a feature. FEATURE may
+be a string naming the feature, otherwise it will be based on
+FILE's name."
+ (let ((basename (file-name-nondirectory file))
+ (lp (if (equal type "package") (setq type "autoloads"))))
(concat ";;; " basename
" --- automatically extracted " (or type "autoloads") "\n"
";;\n"
";;; Code:\n\n"
+ (if lp
+ ;; `load-path' should contain only directory names.
+ "(add-to-list 'load-path (directory-file-name
+ (or (file-name-directory #$) (car load-path))))\n\n")
" \n"
;; This is used outside of autoload.el, eg cus-dep, finder.
- "(provide '"
- (if (stringp feature)
- feature
- (file-name-sans-extension basename))
- ")\n"
+ (if feature
+ (format "(provide '%s)\n"
+ (if (stringp feature) feature
+ (file-name-sans-extension basename))))
";; Local Variables:\n"
";; version-control: never\n"
";; no-byte-compile: t\n"
@@ -351,31 +387,37 @@ not be relied upon."
" ends here\n")))
(defvar autoload-ensure-writable nil
- "Non-nil means `autoload-ensure-default-file' makes existing file writable.")
+ "Non-nil means `autoload-find-generated-file' makes existing file writable.")
;; Just in case someone tries to get you to overwrite a file that you
;; don't want to.
;;;###autoload
(put 'autoload-ensure-writable 'risky-local-variable t)
-(defun autoload-ensure-default-file (file)
- "Make sure that the autoload file FILE exists, creating it if needed.
-If the file already exists and `autoload-ensure-writable' is non-nil,
-make it writable."
- (if (file-exists-p file)
- ;; Probably pointless, but replaces the old AUTOGEN_VCS in lisp/Makefile,
- ;; which was designed to handle CVSREAD=1 and equivalent.
- (and autoload-ensure-writable
- (let ((modes (file-modes file)))
- (if (zerop (logand modes #o0200))
- ;; Ignore any errors here, and let subsequent attempts
- ;; to write the file raise any real error.
- (ignore-errors (set-file-modes file (logior modes #o0200))))))
- (write-region (autoload-rubric file) nil file))
+(defun autoload-ensure-file-writeable (file)
+ ;; Probably pointless, but replaces the old AUTOGEN_VCS in lisp/Makefile,
+ ;; which was designed to handle CVSREAD=1 and equivalent.
+ (and autoload-ensure-writable
+ (file-exists-p file)
+ (let ((modes (file-modes file)))
+ (if (zerop (logand modes #o0200))
+ ;; Ignore any errors here, and let subsequent attempts
+ ;; to write the file raise any real error.
+ (ignore-errors (set-file-modes file (logior modes #o0200))))))
file)
(defun autoload-insert-section-header (outbuf autoloads load-name file time)
"Insert the section-header line,
which lists the file name and which functions are in it, etc."
+ ;; (cl-assert ;Make sure we don't insert it in the middle of another section.
+ ;; (save-excursion
+ ;; (or (not (re-search-backward
+ ;; (concat "\\("
+ ;; (regexp-quote generate-autoload-section-header)
+ ;; "\\)\\|\\("
+ ;; (regexp-quote generate-autoload-section-trailer)
+ ;; "\\)")
+ ;; nil t))
+ ;; (match-end 2))))
(insert generate-autoload-section-header)
(prin1 `(autoloads ,autoloads ,load-name ,file ,time)
outbuf)
@@ -434,7 +476,7 @@ which lists the file name and which functions are in it, etc."
;; without checking its content. This makes it generate wrong load
;; names for cases like lisp/term which is not added to load-path.
(setq dir (expand-file-name (pop names) dir)))
- (t (setq name (mapconcat 'identity names "/")))))
+ (t (setq name (mapconcat #'identity names "/")))))
(if (string-match "\\.elc?\\(\\.\\|\\'\\)" name)
(substring name 0 (match-beginning 0))
name)))
@@ -450,8 +492,119 @@ Return non-nil in the case where no autoloads were added at point."
(let ((generated-autoload-file buffer-file-name))
(autoload-generate-file-autoloads file (current-buffer))))
-(defvar print-readably)
-
+(defvar autoload-compute-prefixes t
+ "If non-nil, autoload will add code to register the prefixes used in a file.
+Standard prefixes won't be registered anyway. I.e. if a file \"foo.el\" defines
+variables or functions that use \"foo-\" as prefix, that will not be registered.
+But all other prefixes will be included.")
+
+(defconst autoload-def-prefixes-max-entries 5
+ "Target length of the list of definition prefixes per file.
+If set too small, the prefixes will be too generic (i.e. they'll use little
+memory, we'll end up looking in too many files when we need a particular
+prefix), and if set too large, they will be too specific (i.e. they will
+cost more memory use).")
+
+(defconst autoload-def-prefixes-max-length 12
+ "Target size of definition prefixes.
+Don't try to split prefixes that are already longer than that.")
+
+(require 'radix-tree)
+
+(defun autoload--make-defs-autoload (defs file)
+
+ ;; Remove the defs that obey the rule that file foo.el (or
+ ;; foo-mode.el) uses "foo-" as prefix.
+ ;; FIXME: help--symbol-completion-table still doesn't know how to use
+ ;; the rule that file foo.el (or foo-mode.el) uses "foo-" as prefix.
+ ;;(let ((prefix
+ ;; (concat (substring file 0 (string-match "-mode\\'" file)) "-")))
+ ;; (dolist (def (prog1 defs (setq defs nil)))
+ ;; (unless (string-prefix-p prefix def)
+ ;; (push def defs))))
+
+ ;; Then compute a small set of prefixes that cover all the
+ ;; remaining definitions.
+ (let* ((tree (let ((tree radix-tree-empty))
+ (dolist (def defs)
+ (setq tree (radix-tree-insert tree def t)))
+ tree))
+ (prefixes nil))
+ ;; Get the root prefixes, that we should include in any case.
+ (radix-tree-iter-subtrees
+ tree (lambda (prefix subtree)
+ (push (cons prefix subtree) prefixes)))
+ ;; In some cases, the root prefixes are too short, e.g. if you define
+ ;; "cc-helper" and "c-mode", you'll get "c" in the root prefixes.
+ (dolist (pair (prog1 prefixes (setq prefixes nil)))
+ (let ((s (car pair)))
+ (if (or (and (> (length s) 2) ; Long enough!
+ ;; But don't use "def" from deffoo-pkg-thing.
+ (not (string= "def" s)))
+ (string-match ".[[:punct:]]\\'" s) ;A real (tho short) prefix?
+ (radix-tree-lookup (cdr pair) "")) ;Nothing to expand!
+ (push pair prefixes) ;Keep it as is.
+ (radix-tree-iter-subtrees
+ (cdr pair) (lambda (prefix subtree)
+ (push (cons (concat s prefix) subtree) prefixes))))))
+ ;; FIXME: The expansions done below are mostly pointless, such as
+ ;; for `yenc', where we replace "yenc-" with an exhaustive list (5
+ ;; elements).
+ ;; (while
+ ;; (let ((newprefixes nil)
+ ;; (changes nil))
+ ;; (dolist (pair prefixes)
+ ;; (let ((prefix (car pair)))
+ ;; (if (or (> (length prefix) autoload-def-prefixes-max-length)
+ ;; (radix-tree-lookup (cdr pair) ""))
+ ;; ;; No point splitting it any further.
+ ;; (push pair newprefixes)
+ ;; (setq changes t)
+ ;; (radix-tree-iter-subtrees
+ ;; (cdr pair) (lambda (sprefix subtree)
+ ;; (push (cons (concat prefix sprefix) subtree)
+ ;; newprefixes))))))
+ ;; (and changes
+ ;; (<= (length newprefixes)
+ ;; autoload-def-prefixes-max-entries)
+ ;; (let ((new nil)
+ ;; (old nil))
+ ;; (dolist (pair prefixes)
+ ;; (unless (memq pair newprefixes) ;Not old
+ ;; (push pair old)))
+ ;; (dolist (pair newprefixes)
+ ;; (unless (memq pair prefixes) ;Not new
+ ;; (push pair new)))
+ ;; (cl-assert new)
+ ;; (message "Expanding %S to %S"
+ ;; (mapcar #'car old) (mapcar #'car new))
+ ;; t)
+ ;; (setq prefixes newprefixes)
+ ;; (< (length prefixes) autoload-def-prefixes-max-entries))))
+
+ ;; (message "Final prefixes %s : %S" file (mapcar #'car prefixes))
+ (when prefixes
+ (let ((strings
+ (mapcar
+ (lambda (x)
+ (let ((prefix (car x)))
+ (if (or (> (length prefix) 2) ;Long enough!
+ (and (eq (length prefix) 2)
+ (string-match "[[:punct:]]" prefix)))
+ prefix
+ ;; Some packages really don't follow the rules.
+ ;; Drop the most egregious cases such as the
+ ;; one-letter prefixes.
+ (let ((dropped ()))
+ (radix-tree-iter-mappings
+ (cdr x) (lambda (s _)
+ (push (concat prefix s) dropped)))
+ (message "Not registering prefix \"%s\" from %s. Affects: %S"
+ prefix file dropped)
+ nil))))
+ prefixes)))
+ `(if (fboundp 'register-definition-prefixes)
+ (register-definition-prefixes ,file ',(delq nil strings)))))))
(defun autoload--setup-output (otherbuf outbuf absfile load-name)
(let ((outbuf
@@ -529,11 +682,11 @@ FILE's modification time."
(let (load-name
(print-length nil)
(print-level nil)
- (print-readably t) ; This does something in Lucid Emacs.
(float-output-format nil)
(visited (get-file-buffer file))
(otherbuf nil)
(absfile (expand-file-name file))
+ (defs '())
;; nil until we found a cookie.
output-start)
(when
@@ -578,27 +731,93 @@ FILE's modification time."
package--builtin-versions))
(princ "\n")))))
- (goto-char (point-min))
- (while (not (eobp))
- (skip-chars-forward " \t\n\f")
- (cond
- ((looking-at (regexp-quote generate-autoload-cookie))
- ;; If not done yet, figure out where to insert this text.
- (unless output-start
- (setq output-start (autoload--setup-output
- otherbuf outbuf absfile load-name)))
- (autoload--print-cookie-text output-start load-name file))
- ((looking-at ";")
- ;; Don't read the comment.
- (forward-line 1))
- (t
- (forward-sexp 1)
- (forward-line 1))))))
+ ;; Do not insert autoload entries for excluded files.
+ (unless (member absfile autoload-excludes)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (skip-chars-forward " \t\n\f")
+ (cond
+ ((looking-at (regexp-quote generate-autoload-cookie))
+ ;; If not done yet, figure out where to insert this text.
+ (unless output-start
+ (setq output-start (autoload--setup-output
+ otherbuf outbuf absfile load-name)))
+ (autoload--print-cookie-text output-start load-name file))
+ ((= (following-char) ?\;)
+ ;; Don't read the comment.
+ (forward-line 1))
+ (t
+ ;; Avoid (defvar <foo>) by requiring a trailing space.
+ ;; Also, ignore this prefix business
+ ;; for ;;;###tramp-autoload and friends.
+ (when (and (equal generate-autoload-cookie ";;;###autoload")
+ (looking-at "(\\(def[^ ]+\\) ['(]*\\([^' ()\"\n]+\\)[\n \t]")
+ (not (member
+ (match-string 1)
+ '("define-obsolete-function-alias"
+ "define-obsolete-variable-alias"
+ "define-category" "define-key"
+ "defgroup" "defface" "defadvice"
+ "def-edebug-spec"
+ ;; Hmm... this is getting ugly:
+ "define-widget"
+ "define-erc-response-handler"
+ "defun-rcirc-command"))))
+ (push (match-string 2) defs))
+ (forward-sexp 1)
+ (forward-line 1)))))))
+
+ (when (and autoload-compute-prefixes defs)
+ ;; This output needs to always go in the main loaddefs.el,
+ ;; regardless of generated-autoload-file.
+ ;; FIXME: the files that don't have autoload cookies but
+ ;; do have definitions end up listed twice in loaddefs.el:
+ ;; once for their register-definition-prefixes and once in
+ ;; the list of "files without any autoloads".
+ (let ((form (autoload--make-defs-autoload defs load-name)))
+ (cond
+ ((null form)) ;All defs obey the default rule, yay!
+ ((not otherbuf)
+ (unless output-start
+ (setq output-start (autoload--setup-output
+ nil outbuf absfile load-name)))
+ (let ((autoload-print-form-outbuf
+ (marker-buffer output-start)))
+ (autoload-print-form form)))
+ (t
+ (let* ((other-output-start
+ ;; To force the output to go to the main loaddefs.el
+ ;; rather than to generated-autoload-file,
+ ;; there are two cases: if outbuf is non-nil,
+ ;; then passing otherbuf=nil is enough, but if
+ ;; outbuf is nil, that won't cut it, so we
+ ;; locally bind generated-autoload-file.
+ (let ((generated-autoload-file
+ (default-value 'generated-autoload-file)))
+ (autoload--setup-output nil outbuf absfile load-name)))
+ (autoload-print-form-outbuf
+ (marker-buffer other-output-start)))
+ (autoload-print-form form)
+ (with-current-buffer (marker-buffer other-output-start)
+ (save-excursion
+ ;; Insert the section-header line which lists
+ ;; the file name and which functions are in it, etc.
+ (goto-char other-output-start)
+ (let ((relfile (file-relative-name absfile)))
+ (autoload-insert-section-header
+ (marker-buffer other-output-start)
+ "actual autoloads are elsewhere" load-name relfile
+ (if autoload-timestamps
+ (nth 5 (file-attributes absfile))
+ autoload--non-timestamp))
+ (insert ";;; Generated autoloads from " relfile "\n")))
+ (insert generate-autoload-section-trailer)))))))
(when output-start
(let ((secondary-autoloads-file-buf
(if otherbuf (current-buffer))))
(with-current-buffer (marker-buffer output-start)
+ (cl-assert (> (point) output-start))
(save-excursion
;; Insert the section-header line which lists the file name
;; and which functions are in it, etc.
@@ -624,7 +843,9 @@ FILE's modification time."
;; We'd really want to just use
;; `emacs-internal' instead.
nil nil 'emacs-mule-unix)
- (nth 5 (file-attributes relfile))))
+ (if autoload-timestamps
+ (nth 5 (file-attributes relfile))
+ autoload--non-timestamp)))
(insert ";;; Generated autoloads from " relfile "\n")))
(insert generate-autoload-section-trailer))))
(or noninteractive
@@ -649,12 +870,29 @@ FILE's modification time."
(error "%s:0:0: error: %s: %s" file (car err) (cdr err)))
))
+;; For parallel builds, to stop another process reading a half-written file.
+(defun autoload--save-buffer ()
+ "Save current buffer to its file, atomically."
+ ;; Copied from byte-compile-file.
+ (let* ((version-control 'never)
+ (tempfile (make-temp-name buffer-file-name))
+ (kill-emacs-hook
+ (cons (lambda () (ignore-errors (delete-file tempfile)))
+ kill-emacs-hook)))
+ (write-region (point-min) (point-max) tempfile nil 1)
+ (backup-buffer)
+ (rename-file tempfile buffer-file-name t)
+ (set-buffer-modified-p nil)
+ (set-visited-file-modtime)
+ (or noninteractive (message "Wrote %s" buffer-file-name))))
+
(defun autoload-save-buffers ()
(while autoload-modified-buffers
(with-current-buffer (pop autoload-modified-buffers)
- (let ((version-control 'never))
- (save-buffer)))))
+ (autoload--save-buffer))))
+;; FIXME This command should be deprecated.
+;; See http://debbugs.gnu.org/22213#41
;;;###autoload
(defun update-file-autoloads (file &optional save-after outfile)
"Update the autoloads for FILE.
@@ -672,6 +910,9 @@ Return FILE if there was no autoload cookie in it, else nil."
(read-file-name "Write autoload definitions to file: ")))
(let* ((generated-autoload-file (or outfile generated-autoload-file))
(autoload-modified-buffers nil)
+ ;; We need this only if the output file handles more than one input.
+ ;; See http://debbugs.gnu.org/22213#38 and subsequent.
+ (autoload-timestamps t)
(no-autoloads (autoload-generate-file-autoloads file)))
(if autoload-modified-buffers
(if save-after (autoload-save-buffers))
@@ -689,6 +930,9 @@ removes any prior now out-of-date autoload entries."
(catch 'up-to-date
(let* ((buf (current-buffer))
(existing-buffer (if buffer-file-name buf))
+ (output-file (autoload-generated-file))
+ (output-time (if (file-exists-p output-file)
+ (nth 5 (file-attributes output-file))))
(found nil))
(with-current-buffer (autoload-find-generated-file)
;; This is to make generated-autoload-file have Unix EOLs, so
@@ -713,16 +957,28 @@ removes any prior now out-of-date autoload entries."
(file-time (nth 5 (file-attributes file))))
(if (and (or (null existing-buffer)
(not (buffer-modified-p existing-buffer)))
- (or
+ (cond
+ ;; FIXME? Arguably we should throw a
+ ;; user error, or some kind of warning,
+ ;; if we were called from update-file-autoloads,
+ ;; which can update only a single input file.
+ ;; It's not appropriate to use the output
+ ;; file modtime in such a case,
+ ;; if there are multiple input files
+ ;; contributing to the output.
+ ((and output-time
+ (member last-time
+ (list t autoload--non-timestamp)))
+ (not (time-less-p output-time file-time)))
;; last-time is the time-stamp (specifying
;; the last time we looked at the file) and
;; the file hasn't been changed since.
- (and (listp last-time)
- (not (time-less-p last-time file-time)))
+ ((listp last-time)
+ (not (time-less-p last-time file-time)))
;; last-time is an MD5 checksum instead.
- (and (stringp last-time)
- (equal last-time
- (md5 buf nil nil 'emacs-mule)))))
+ ((stringp last-time)
+ (equal last-time
+ (md5 buf nil nil 'emacs-mule)))))
(throw 'up-to-date nil)
(autoload-remove-section begin)
(setq found t))))
@@ -773,12 +1029,13 @@ write its autoloads into the specified file instead."
(unless (string-match "\\.\\(elc\\|\\so\\|dll\\)" suf)
(push suf tmp)))
(concat "^[^=.].*" (regexp-opt tmp t) "\\'")))
- (files (apply 'nconc
+ (files (apply #'nconc
(mapcar (lambda (dir)
(directory-files (expand-file-name dir)
t files-re))
dirs)))
- (done ())
+ (done ()) ;Files processed; to remove duplicates.
+ (changed nil) ;Non-nil if some change occurred.
(last-time)
;; Files with no autoload cookies or whose autoloads go to other
;; files because of file-local autoload-generated-file settings.
@@ -787,13 +1044,16 @@ write its autoloads into the specified file instead."
(generated-autoload-file
(if (called-interactively-p 'interactive)
(read-file-name "Write autoload definitions to file: ")
- generated-autoload-file)))
+ generated-autoload-file))
+ (output-time
+ (if (file-exists-p generated-autoload-file)
+ (nth 5 (file-attributes generated-autoload-file)))))
(with-current-buffer (autoload-find-generated-file)
(save-excursion
;; Canonicalize file names and remove the autoload file itself.
(setq files (delete (file-relative-name buffer-file-name)
- (mapcar 'file-relative-name files)))
+ (mapcar #'file-relative-name files)))
(goto-char (point-min))
(while (search-forward generate-autoload-section-header nil t)
@@ -805,28 +1065,33 @@ write its autoloads into the specified file instead."
;; Remove the obsolete section.
(autoload-remove-section (match-beginning 0))
(setq last-time (nth 4 form))
- (when (listp last-time)
- (dolist (file file)
- (let ((file-time (nth 5 (file-attributes file))))
- (when (and file-time
- (not (time-less-p last-time file-time)))
- ;; file unchanged
- (push file no-autoloads)
- (setq files (delete file files)))))))
+ (if (member last-time (list t autoload--non-timestamp))
+ (setq last-time output-time))
+ (dolist (file file)
+ (let ((file-time (nth 5 (file-attributes file))))
+ (when (and file-time
+ (not (time-less-p last-time file-time)))
+ ;; file unchanged
+ (push file no-autoloads)
+ (setq files (delete file files))))))
((not (stringp file)))
((or (not (file-exists-p file))
;; Remove duplicates as well, just in case.
- (member file done)
- ;; If the file is actually excluded.
- (member (expand-file-name file) autoload-excludes))
+ (member file done))
;; Remove the obsolete section.
+ (setq changed t)
(autoload-remove-section (match-beginning 0)))
- ((and (listp (nth 4 form))
- (not (time-less-p (nth 4 form)
- (nth 5 (file-attributes file)))))
+ ((not (time-less-p (let ((oldtime (nth 4 form)))
+ (if (member oldtime
+ (list
+ t autoload--non-timestamp))
+ output-time
+ oldtime))
+ (nth 5 (file-attributes file))))
;; File hasn't changed.
nil)
(t
+ (setq changed t)
(autoload-remove-section (match-beginning 0))
(if (autoload-generate-file-autoloads
;; Passing `current-buffer' makes it insert at point.
@@ -838,7 +1103,6 @@ write its autoloads into the specified file instead."
(let ((no-autoloads-time (or last-time '(0 0 0 0))) file-time)
(dolist (file files)
(cond
- ((member (expand-file-name file) autoload-excludes) nil)
;; Passing nil as second argument forces
;; autoload-generate-file-autoloads to look for the right
;; spot where to insert each autoloads section.
@@ -846,7 +1110,8 @@ write its autoloads into the specified file instead."
(autoload-generate-file-autoloads file nil buffer-file-name))
(push file no-autoloads)
(if (time-less-p no-autoloads-time file-time)
- (setq no-autoloads-time file-time)))))
+ (setq no-autoloads-time file-time)))
+ (t (setq changed t))))
(when no-autoloads
;; Sort them for better readability.
@@ -855,11 +1120,17 @@ write its autoloads into the specified file instead."
(goto-char (point-max))
(search-backward "\f" nil t)
(autoload-insert-section-header
- (current-buffer) nil nil no-autoloads no-autoloads-time)
+ (current-buffer) nil nil no-autoloads (if autoload-timestamps
+ no-autoloads-time
+ autoload--non-timestamp))
(insert generate-autoload-section-trailer)))
- (let ((version-control 'never))
- (save-buffer))
+ ;; Don't modify the file if its content has not been changed, so `make'
+ ;; dependencies don't trigger unnecessarily.
+ (if (not changed)
+ (set-buffer-modified-p nil)
+ (autoload--save-buffer))
+
;; In case autoload entries were added to other files because of
;; file-local autoload-generated-file settings.
(autoload-save-buffers))))
@@ -891,7 +1162,7 @@ should be non-nil)."
(push (expand-file-name file) autoload-excludes)))))))
(let ((args command-line-args-left))
(setq command-line-args-left nil)
- (apply 'update-directory-autoloads args)))
+ (apply #'update-directory-autoloads args)))
(provide 'autoload)
diff --git a/lisp/emacs-lisp/avl-tree.el b/lisp/emacs-lisp/avl-tree.el
index 66fe9796623..17f1ffa9f61 100644
--- a/lisp/emacs-lisp/avl-tree.el
+++ b/lisp/emacs-lisp/avl-tree.el
@@ -98,7 +98,8 @@
;; avl-tree-right avl-tree-data] branch) node)
"Get value of a branch of a node.
NODE is the node, and BRANCH is the branch.
-0 for left pointer, 1 for right pointer and 2 for the data.")
+0 for left pointer, 1 for right pointer and 2 for the data.
+\n(fn BRANCH NODE)")
;; The funcall/aref trick wouldn't work for the setf method, unless we
@@ -400,7 +401,8 @@ itself."
reverse store)
(defalias 'avl-tree-stack-p #'avl-tree--stack-p
- "Return t if argument is an avl-tree-stack, nil otherwise.")
+ "Return t if OBJ is an avl-tree-stack, nil otherwise.
+\n(fn OBJ)")
(defun avl-tree--stack-repopulate (stack)
;; Recursively push children of the node at the head of STACK onto the
@@ -419,12 +421,12 @@ itself."
(defalias 'avl-tree-create #'avl-tree--create
"Create an empty AVL tree.
COMPARE-FUNCTION is a function which takes two arguments, A and B,
-and returns non-nil if A is less than B, and nil otherwise.")
+and returns non-nil if A is less than B, and nil otherwise.
+\n(fn COMPARE-FUNCTION)")
(defalias 'avl-tree-compare-function #'avl-tree--cmpfun
"Return the comparison function for the AVL tree TREE.
-
-\(fn TREE)")
+\n(fn TREE)")
(defun avl-tree-empty (tree)
"Return t if AVL tree TREE is empty, otherwise return nil."
diff --git a/lisp/emacs-lisp/backquote.el b/lisp/emacs-lisp/backquote.el
index 94c561cba0a..bb877dd2c97 100644
--- a/lisp/emacs-lisp/backquote.el
+++ b/lisp/emacs-lisp/backquote.el
@@ -247,4 +247,14 @@ LEVEL is only used internally and indicates the nesting level:
tail))
(t (cons 'list heads)))))
+
+;; Give `,' and `,@' documentation strings which can be examined by C-h f.
+(put '\, 'function-documentation
+ "See `\\=`' (also `pcase') for the usage of `,'.")
+(put '\, 'reader-construct t)
+
+(put '\,@ 'function-documentation
+ "See `\\=`' for the usage of `,@'.")
+(put '\,@ 'reader-construct t)
+
;;; backquote.el ends here
diff --git a/lisp/emacs-lisp/benchmark.el b/lisp/emacs-lisp/benchmark.el
index 25eddf5f6b0..a2217d20953 100644
--- a/lisp/emacs-lisp/benchmark.el
+++ b/lisp/emacs-lisp/benchmark.el
@@ -1,4 +1,4 @@
-;;; benchmark.el --- support for benchmarking code
+;;; benchmark.el --- support for benchmarking code -*- lexical-binding: t -*-
;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
@@ -33,6 +33,7 @@
(defmacro benchmark-elapse (&rest forms)
"Return the time in seconds elapsed for execution of FORMS."
+ (declare (indent 0) (debug t))
(let ((t1 (make-symbol "t1"))
(t2 (make-symbol "t2")))
`(let (,t1 ,t2)
@@ -41,9 +42,6 @@
(setq ,t2 (current-time))
(float-time (time-subtract ,t2 ,t1)))))
-(put 'benchmark-elapse 'edebug-form-spec t)
-(put 'benchmark-elapse 'lisp-indent-function 0)
-
;;;###autoload
(defmacro benchmark-run (&optional repetitions &rest forms)
"Time execution of FORMS.
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index eac59ecde8b..962a7ae5cde 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -185,6 +185,7 @@
(require 'bytecomp)
(eval-when-compile (require 'cl-lib))
(require 'macroexp)
+(eval-when-compile (require 'subr-x))
(defun byte-compile-log-lap-1 (format &rest args)
;; Newer byte codes for stack-ref make the slot 0 non-nil again.
@@ -288,8 +289,8 @@
(if (eq (car-safe newfn) 'function)
(byte-compile-unfold-lambda `(,(cadr newfn) ,@(cdr form)))
;; This can happen because of macroexp-warn-and-return &co.
- (byte-compile-log-warning
- (format "Inlining closure %S failed" name))
+ (byte-compile-warn
+ "Inlining closure %S failed" name)
form))))
(_ ;; Give up on inlining.
@@ -1209,8 +1210,9 @@
radians-to-degrees rassq rassoc read-from-string regexp-quote
region-beginning region-end reverse round
sin sqrt string string< string= string-equal string-lessp string-to-char
- string-to-int string-to-number substring sxhash symbol-function
- symbol-name symbol-plist symbol-value string-make-unibyte
+ string-to-int string-to-number substring
+ sxhash sxhash-equal sxhash-eq sxhash-eql
+ symbol-function symbol-name symbol-plist symbol-value string-make-unibyte
string-make-multibyte string-as-multibyte string-as-unibyte
string-to-multibyte
tan truncate
@@ -1245,7 +1247,7 @@
hash-table-p
identity ignore integerp integer-or-marker-p interactive-p
invocation-directory invocation-name
- keymapp
+ keymapp keywordp
line-beginning-position line-end-position list listp
make-marker mark mark-marker markerp max-char
memory-limit minibuffer-window
@@ -1355,7 +1357,7 @@
(defun byte-decompile-bytecode-1 (bytes constvec &optional make-spliceable)
(let ((length (length bytes))
(bytedecomp-ptr 0) optr tags bytedecomp-op offset
- lap tmp)
+ lap tmp last-constant)
(while (not (= bytedecomp-ptr length))
(or make-spliceable
(push bytedecomp-ptr lap))
@@ -1384,7 +1386,8 @@
(or (assq tmp byte-compile-variables)
(let ((new (list tmp)))
(push new byte-compile-variables)
- new)))))
+ new)))
+ last-constant tmp))
((eq bytedecomp-op 'byte-stack-set2)
(setq bytedecomp-op 'byte-stack-set))
((and (eq bytedecomp-op 'byte-discardN) (>= offset #x80))
@@ -1393,7 +1396,34 @@
;; lapcode, we represent this by using a different opcode
;; (with the flag removed from the operand).
(setq bytedecomp-op 'byte-discardN-preserve-tos)
- (setq offset (- offset #x80))))
+ (setq offset (- offset #x80)))
+ ((eq bytedecomp-op 'byte-switch)
+ (cl-assert (hash-table-p last-constant) nil
+ "byte-switch used without preceeding hash table")
+ ;; We cannot use the original hash table referenced in the op,
+ ;; so we create a copy of it, and replace the addresses with
+ ;; TAGs.
+ (let ((orig-table last-constant))
+ (cl-loop for e across constvec
+ when (eq e last-constant)
+ do (setq last-constant (copy-hash-table e))
+ and return nil)
+ ;; Replace all addresses with TAGs.
+ (maphash #'(lambda (value tag)
+ (let (newtag)
+ (setq newtag (byte-compile-make-tag))
+ (push (cons tag newtag) tags)
+ (puthash value newtag last-constant)))
+ last-constant)
+ ;; Replace the hash table referenced in the lapcode with our
+ ;; modified one.
+ (cl-loop for el in-ref lap
+ when (and (listp el) ;; make sure we're at the correct op
+ (eq (nth 1 el) 'byte-constant)
+ (eq (nth 2 el) orig-table))
+ ;; Jump tables are never reused, so do this exactly
+ ;; once.
+ do (setf (nth 2 el) last-constant) and return nil))))
;; lap = ( [ (pc . (op . arg)) ]* )
(push (cons optr (cons bytedecomp-op (or offset 0)))
lap)
@@ -1722,12 +1752,25 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(setcdr tmp2 lap1)
(setq tmp3 (cdr (memq tmp2 tmp3))))
(setq lap (delq lap0 lap)
- keep-going t))
+ keep-going t)
+ ;; replace references to tag in jump tables, if any
+ (dolist (table byte-compile-jump-tables)
+ (catch 'break
+ (maphash #'(lambda (value tag)
+ (when (equal tag lap0)
+ ;; each tag occurs only once in the jump table
+ (puthash value lap1 table)
+ (throw 'break nil)))
+ table))))
;;
;; unused-TAG: --> <deleted>
;;
((and (eq 'TAG (car lap0))
- (not (rassq lap0 lap)))
+ (not (rassq lap0 lap))
+ ;; make sure this tag isn't used in a jump-table
+ (cl-loop for table in byte-compile-jump-tables
+ when (member lap0 (hash-table-values table))
+ return nil finally return t))
(and (memq byte-optimize-log '(t byte))
(byte-compile-log " unused tag %d removed" (nth 1 lap0)))
(setq lap (delq lap0 lap)
@@ -1735,9 +1778,15 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
;;
;; goto ... --> goto <delete until TAG or end>
;; return ... --> return <delete until TAG or end>
- ;;
+ ;; (unless a jump-table is being used, where deleting may affect
+ ;; other valid case bodies)
+ ;;
((and (memq (car lap0) '(byte-goto byte-return))
- (not (memq (car lap1) '(TAG nil))))
+ (not (memq (car lap1) '(TAG nil)))
+ ;; FIXME: Instead of deferring simply when jump-tables are
+ ;; being used, keep a list of tags used for switch tags and
+ ;; use them instead (see `byte-compile-inline-lapcode').
+ (not byte-compile-jump-tables))
(setq tmp rest)
(let ((i 0)
(opt-p (memq byte-optimize-log '(t lap)))
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index de6755a41c7..4fa31dd4c27 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -240,6 +240,11 @@ The return value is undefined.
;; from
;; (defun foo (arg) (toto)).
(declare (doc-string 3) (indent 2))
+ (or name (error "Cannot define '%s' as a function" name))
+ (if (null
+ (and (listp arglist)
+ (null (delq t (mapcar #'symbolp arglist)))))
+ (error "Malformed arglist: %s" arglist))
(let ((decls (cond
((eq (car-safe docstring) 'declare)
(prog1 (cdr docstring) (setq docstring nil)))
@@ -469,7 +474,7 @@ load time. In interpreted code, this is entirely equivalent to
`progn', except that the value of the expression may be (but is
not necessarily) computed at load time if eager macro expansion
is enabled."
- (declare (debug t) (indent 0))
+ (declare (debug (&rest def-form)) (indent 0))
;; When the byte-compiler expands code, this macro is not used, so we're
;; either about to run `body' (plain interpretation) or we're doing eager
;; macroexpansion.
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 2c808a5b4bd..e5b9b47b1d0 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -124,11 +124,13 @@
(require 'backquote)
(require 'macroexp)
(require 'cconv)
+(require 'cl-lib)
;; During bootstrap, cl-loaddefs.el is not created yet, so loading cl-lib
;; doesn't setup autoloads for things like cl-every, which is why we have to
-;; require cl-extra instead (bug#18804).
-(require 'cl-extra)
+;; require cl-extra as well (bug#18804).
+(or (fboundp 'cl-every)
+ (require 'cl-extra))
(or (fboundp 'defsubst)
;; This really ought to be loaded already!
@@ -164,24 +166,19 @@ file name, and return the name of the compiled file."
(funcall handler 'byte-compiler-base-file-name filename)
filename)))
-(or (fboundp 'byte-compile-dest-file)
- ;; The user may want to redefine this along with emacs-lisp-file-regexp,
- ;; so only define it if it is undefined.
- ;; Note - redefining this function is obsolete as of 23.2.
- ;; Customize byte-compile-dest-file-function instead.
- (defun byte-compile-dest-file (filename)
- "Convert an Emacs Lisp source file name to a compiled file name.
+(defun byte-compile-dest-file (filename)
+ "Convert an Emacs Lisp source file name to a compiled file name.
If `byte-compile-dest-file-function' is non-nil, uses that
function to do the work. Otherwise, if FILENAME matches
`emacs-lisp-file-regexp' (by default, files with the extension `.el'),
adds `c' to it; otherwise adds `.elc'."
- (if byte-compile-dest-file-function
- (funcall byte-compile-dest-file-function filename)
- (setq filename (file-name-sans-versions
- (byte-compiler-base-file-name filename)))
- (cond ((string-match emacs-lisp-file-regexp filename)
- (concat (substring filename 0 (match-beginning 0)) ".elc"))
- (t (concat filename ".elc"))))))
+ (if byte-compile-dest-file-function
+ (funcall byte-compile-dest-file-function filename)
+ (setq filename (file-name-sans-versions
+ (byte-compiler-base-file-name filename)))
+ (cond ((string-match emacs-lisp-file-regexp filename)
+ (concat (substring filename 0 (match-beginning 0)) ".elc"))
+ (t (concat filename ".elc")))))
;; This can be the 'byte-compile property of any symbol.
(autoload 'byte-compile-inline-expand "byte-opt")
@@ -223,6 +220,11 @@ This includes variable references and calls to functions such as `car'."
:group 'bytecomp
:type 'boolean)
+(defcustom byte-compile-cond-use-jump-table t
+ "Compile `cond' clauses to a jump table implementation (using a hash-table)."
+ :group 'bytecomp
+ :type 'boolean)
+
(defvar byte-compile-dynamic nil
"If non-nil, compile function bodies so they load lazily.
They are hidden in comments in the compiled file,
@@ -413,6 +415,8 @@ specify different fields to sort on."
(defvar byte-compile-debug nil
"If non-nil, byte compile errors will be raised as signals instead of logged.")
+(defvar byte-compile-jump-tables nil
+ "List of all jump tables used during compilation of this form.")
(defvar byte-compile-constants nil
"List of all constants encountered during compilation of this form.")
(defvar byte-compile-variables nil
@@ -436,7 +440,7 @@ Return the compile-time value of FORM."
;; Macroexpand (not macroexpand-all!) form at toplevel in case it
;; expands into a toplevel-equivalent `progn'. See CLHS section
;; 3.2.3.1, "Processing of Top Level Forms". The semantics are very
- ;; subtle: see test/automated/bytecomp-tests.el for interesting
+ ;; subtle: see test/lisp/emacs-lisp/bytecomp-tests.el for interesting
;; cases.
(setf form (macroexp-macroexpand form byte-compile-macro-environment))
(if (eq (car-safe form) 'progn)
@@ -748,6 +752,10 @@ otherwise pop it")
;; `byte-compile-lapcode').
(defconst byte-discardN-preserve-tos byte-discardN)
+(byte-defop 183 -2 byte-switch
+ "to take a hash table and a value from the stack, and jump to the address
+the value maps to, if any.")
+
;; unused: 182-191
(byte-defop 192 1 byte-constant "for reference to a constant")
@@ -824,7 +832,7 @@ CONST2 may be evaluated multiple times."
op off ; Operation & offset
opcode ; numeric value of OP
(bytes '()) ; Put the output bytes here
- (patchlist nil)) ; List of gotos to patch
+ (patchlist nil)) ; List of gotos to patch
(dolist (lap-entry lap)
(setq op (car lap-entry)
off (cdr lap-entry))
@@ -901,11 +909,22 @@ CONST2 may be evaluated multiple times."
;; Patch tag PCs into absolute jumps.
(dolist (bytes-tail patchlist)
(setq pc (caar bytes-tail)) ; Pick PC from goto's tag.
+ ;; Splits PC's value into 2 bytes. The jump address is
+ ;; "reconstructed" by the `FETCH2' macro in `bytecode.c'.
(setcar (cdr bytes-tail) (logand pc 255))
(setcar bytes-tail (lsh pc -8))
;; FIXME: Replace this by some workaround.
(if (> (car bytes-tail) 255) (error "Bytecode overflow")))
+ ;; Similarly, replace TAGs in all jump tables with the correct PC index.
+ (dolist (hash-table byte-compile-jump-tables)
+ (maphash #'(lambda (value tag)
+ (setq pc (cadr tag))
+ ;; We don't need to split PC here, as it is stored as a lisp
+ ;; object in the hash table (whereas other goto-* ops store
+ ;; it within 2 bytes in the byte string).
+ (puthash value pc hash-table))
+ hash-table))
(apply 'unibyte-string (nreverse bytes))))
@@ -1023,39 +1042,42 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
(setcdr list (cddr list)))
total)))
-;; The purpose of this function is to iterate through the
-;; `read-symbol-positions-list'. Each time we process, say, a
-;; function definition (`defun') we remove `defun' from
-;; `read-symbol-positions-list', and set `byte-compile-last-position'
-;; to that symbol's character position. Similarly, if we encounter a
-;; variable reference, like in (1+ foo), we remove `foo' from the
-;; list. If our current position is after the symbol's position, we
-;; assume we've already passed that point, and look for the next
-;; occurrence of the symbol.
+;; The purpose of `byte-compile-set-symbol-position' is to attempt to
+;; set `byte-compile-last-position' to the "current position" in the
+;; raw source code. This is used for warning and error messages.
+;;
+;; The function should be called for most occurrences of symbols in
+;; the forms being compiled, strictly in the order they occur in the
+;; source code. It should never be called twice for any single
+;; occurrence, and should not be called for symbols generated by the
+;; byte compiler itself.
;;
-;; This function should not be called twice for the same occurrence of
-;; a symbol, and it should not be called for symbols generated by the
-;; byte compiler itself; because rather than just fail looking up the
-;; symbol, we may find an occurrence of the symbol further ahead, and
-;; then `byte-compile-last-position' as advanced too far.
+;; The function works by scanning the elements in the alist
+;; `read-symbol-positions-list' for the next match for the symbol
+;; after the current value of `byte-compile-last-position', setting
+;; that variable to the match's character position, then deleting the
+;; matching element from the list. Thus the new value for
+;; `byte-compile-last-position' is later than the old value unless,
+;; perhaps, ALLOW-PREVIOUS is non-nil.
;;
-;; So your're probably asking yourself: Isn't this function a
-;; gross hack? And the answer, of course, would be yes.
+;; So your're probably asking yourself: Isn't this function a gross
+;; hack? And the answer, of course, would be yes.
(defun byte-compile-set-symbol-position (sym &optional allow-previous)
(when byte-compile-read-position
- (let (last entry)
+ (let ((last byte-compile-last-position)
+ entry)
(while (progn
- (setq last byte-compile-last-position
- entry (assq sym read-symbol-positions-list))
+ (setq entry (assq sym read-symbol-positions-list))
(when entry
(setq byte-compile-last-position
(+ byte-compile-read-position (cdr entry))
read-symbol-positions-list
(byte-compile-delete-first
entry read-symbol-positions-list)))
- (or (and allow-previous
- (not (= last byte-compile-last-position)))
- (> last byte-compile-last-position)))))))
+ (and entry
+ (or (and allow-previous
+ (not (= last byte-compile-last-position)))
+ (> last byte-compile-last-position))))))))
(defvar byte-compile-last-warned-form nil)
(defvar byte-compile-last-logged-file nil)
@@ -1161,9 +1183,13 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
(compilation-forget-errors)
pt))))
-;; Log a message STRING in `byte-compile-log-buffer'.
-;; Also log the current function and file if not already done.
(defun byte-compile-log-warning (string &optional fill level)
+ "Log a message STRING in `byte-compile-log-buffer'.
+Also log the current function and file if not already done. If
+FILL is non-nil, set `warning-fill-prefix' to four spaces. LEVEL
+is the warning level (`:warning' or `:error'). Do not call this
+function directly; use `byte-compile-warn' or
+`byte-compile-report-error' instead."
(let ((warning-prefix-function 'byte-compile-warning-prefix)
(warning-type-format "")
(warning-fill-prefix (if fill " ")))
@@ -1187,15 +1213,16 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
(unless (and funcp (memq symbol byte-compile-not-obsolete-funcs))
(byte-compile-warn "%s" msg)))))
-(defun byte-compile-report-error (error-info)
+(defun byte-compile-report-error (error-info &optional fill)
"Report Lisp error in compilation.
ERROR-INFO is the error data, in the form of either (ERROR-SYMBOL . DATA)
-or STRING."
+or STRING. If FILL is non-nil, set ‘warning-fill-prefix’ to four spaces
+when printing the error message."
(setq byte-compiler-error-flag t)
(byte-compile-log-warning
(if (stringp error-info) error-info
(error-message-string error-info))
- nil :error))
+ fill :error))
;;; sanity-checking arglists
@@ -1280,6 +1307,7 @@ or STRING."
(t (format "%d-%d" (car signature) (cdr signature)))))
(defun byte-compile-function-warn (f nargs def)
+ (byte-compile-set-symbol-position f)
(when (get f 'byte-obsolete-info)
(byte-compile-warn-obsolete f))
@@ -1347,10 +1375,15 @@ extra args."
(let ((nfields (with-temp-buffer
(insert (nth 1 form))
(goto-char (point-min))
- (let ((n 0))
+ (let ((i 0) (n 0))
(while (re-search-forward "%." nil t)
- (unless (eq ?% (char-after (1+ (match-beginning 0))))
- (setq n (1+ n))))
+ (backward-char)
+ (unless (eq ?% (char-after))
+ (setq i (if (looking-at "\\([0-9]+\\)\\$")
+ (string-to-number (match-string 1) 10)
+ (1+ i))
+ n (max n i)))
+ (forward-char))
n)))
(nargs (- (length form) 2)))
(unless (= nargs nfields)
@@ -1361,31 +1394,33 @@ extra args."
(dolist (elt '(format message error))
(put elt 'byte-compile-format-like t))
-;; Warn if a custom definition fails to specify :group.
+;; Warn if a custom definition fails to specify :group, or :type.
(defun byte-compile-nogroup-warn (form)
- (if (and (memq (car form) '(custom-declare-face custom-declare-variable))
- byte-compile-current-group)
- ;; The group will be provided implicitly.
- nil
- (let ((keyword-args (cdr (cdr (cdr (cdr form)))))
- (name (cadr form)))
- (or (not (eq (car-safe name) 'quote))
- (and (eq (car form) 'custom-declare-group)
- (equal name ''emacs))
- (plist-get keyword-args :group)
- (not (and (consp name) (eq (car name) 'quote)))
- (byte-compile-warn
- "%s for `%s' fails to specify containing group"
- (cdr (assq (car form)
- '((custom-declare-group . defgroup)
- (custom-declare-face . defface)
- (custom-declare-variable . defcustom))))
- (cadr name)))
- ;; Update the current group, if needed.
- (if (and byte-compile-current-file ;Only when compiling a whole file.
- (eq (car form) 'custom-declare-group)
- (eq (car-safe name) 'quote))
- (setq byte-compile-current-group (cadr name))))))
+ (let ((keyword-args (cdr (cdr (cdr (cdr form)))))
+ (name (cadr form)))
+ (when (eq (car-safe name) 'quote)
+ (or (not (eq (car form) 'custom-declare-variable))
+ (plist-get keyword-args :type)
+ (byte-compile-warn
+ "defcustom for `%s' fails to specify type" (cadr name)))
+ (if (and (memq (car form) '(custom-declare-face custom-declare-variable))
+ byte-compile-current-group)
+ ;; The group will be provided implicitly.
+ nil
+ (or (and (eq (car form) 'custom-declare-group)
+ (equal name ''emacs))
+ (plist-get keyword-args :group)
+ (byte-compile-warn
+ "%s for `%s' fails to specify containing group"
+ (cdr (assq (car form)
+ '((custom-declare-group . defgroup)
+ (custom-declare-face . defface)
+ (custom-declare-variable . defcustom))))
+ (cadr name)))
+ ;; Update the current group, if needed.
+ (if (and byte-compile-current-file ;Only when compiling a whole file.
+ (eq (car form) 'custom-declare-group))
+ (setq byte-compile-current-group (cadr name)))))))
;; Warn if the function or macro is being redefined with a different
;; number of arguments.
@@ -1629,7 +1664,12 @@ that already has a `.elc' file."
(if arg (setq arg (prefix-numeric-value arg)))
(if noninteractive
nil
- (save-some-buffers)
+ (save-some-buffers
+ nil (lambda ()
+ (let ((file (buffer-file-name)))
+ (and file
+ (string-match-p emacs-lisp-file-regexp file)
+ (file-in-directory-p file directory)))))
(force-mode-line-update))
(with-current-buffer (get-buffer-create byte-compile-log-buffer)
(setq default-directory (expand-file-name directory))
@@ -1882,12 +1922,13 @@ The value is non-nil if there were no errors, nil if errors."
(rename-file tempfile target-file t)
(or noninteractive (message "Wrote %s" target-file)))
;; This is just to give a better error message than write-region
- (signal 'file-error
- (list "Opening output file"
- (if (file-exists-p target-file)
- "Cannot overwrite file"
- "Directory not writable or nonexistent")
- target-file)))
+ (let ((exists (file-exists-p target-file)))
+ (signal (if exists 'file-error 'file-missing)
+ (list "Opening output file"
+ (if exists
+ "Cannot overwrite file"
+ "Directory not writable or nonexistent")
+ target-file))))
(kill-buffer (current-buffer)))
(if (and byte-compile-generate-call-tree
(or (eq t byte-compile-generate-call-tree)
@@ -1943,7 +1984,8 @@ With argument ARG, insert value in current buffer after the form."
;; (edebug-all-defs nil)
;; (edebug-all-forms nil)
;; Simulate entry to byte-compile-top-level
- (byte-compile-constants nil)
+ (byte-compile-jump-tables nil)
+ (byte-compile-constants nil)
(byte-compile-variables nil)
(byte-compile-tag-number 0)
(byte-compile-depth 0)
@@ -1984,18 +2026,25 @@ With argument ARG, insert value in current buffer after the form."
;; Compile the forms from the input buffer.
(while (progn
(while (progn (skip-chars-forward " \t\n\^l")
- (looking-at ";"))
+ (= (following-char) ?\;))
(forward-line 1))
(not (eobp)))
(setq byte-compile-read-position (point)
byte-compile-last-position byte-compile-read-position)
- (let* ((old-style-backquotes nil)
+ (let* ((lread--old-style-backquotes nil)
+ (lread--unescaped-character-literals nil)
(form (read inbuffer)))
;; Warn about the use of old-style backquotes.
- (when old-style-backquotes
+ (when lread--old-style-backquotes
(byte-compile-warn "!! The file uses old-style backquotes !!
This functionality has been obsolete for more than 10 years already
and will be removed soon. See (elisp)Backquote in the manual."))
+ (when lread--unescaped-character-literals
+ (byte-compile-warn
+ "unescaped character literals %s detected!"
+ (mapconcat (lambda (char) (format "`?%c'" char))
+ (sort lread--unescaped-character-literals #'<)
+ ", ")))
(byte-compile-toplevel-file-form form)))
;; Compile pending forms at end of file.
(byte-compile-flush-pending)
@@ -2239,7 +2288,8 @@ list that represents a doc string reference.
byte-compile-variables nil
byte-compile-depth 0
byte-compile-maxdepth 0
- byte-compile-output nil))))
+ byte-compile-output nil
+ byte-compile-jump-tables nil))))
(defvar byte-compile-force-lexical-warnings nil)
@@ -2581,7 +2631,13 @@ FUN should be either a `lambda' value or a `closure' value."
(pcase-let* (((or (and `(lambda ,args . ,body) (let env nil))
`(closure ,env ,args . ,body))
fun)
+ (preamble nil)
(renv ()))
+ ;; Split docstring and `interactive' form from body.
+ (when (stringp (car body))
+ (push (pop body) preamble))
+ (when (eq (car-safe (car body)) 'interactive)
+ (push (pop body) preamble))
;; Turn the function's closed vars (if any) into local let bindings.
(dolist (binding env)
(cond
@@ -2594,8 +2650,8 @@ FUN should be either a `lambda' value or a `closure' value."
((eq binding t))
(t (push `(defvar ,binding) body))))
(if (null renv)
- `(lambda ,args ,@body)
- `(lambda ,args (let ,(nreverse renv) ,@body)))))
+ `(lambda ,args ,@preamble ,@body)
+ `(lambda ,args ,@preamble (let ,(nreverse renv) ,@body)))))
;;;###autoload
(defun byte-compile (form)
@@ -2655,8 +2711,11 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(when (cddr list)
(error "Garbage following &rest VAR in lambda-list")))
((eq arg '&optional)
- (unless (cdr list)
- (error "Variable name missing after &optional")))
+ (when (or (null (cdr list))
+ (memq (cadr list) '(&optional &rest)))
+ (error "Variable name missing after &optional"))
+ (when (memq '&optional (cddr list))
+ (error "Duplicate &optional")))
((memq arg vars)
(byte-compile-warn "repeated variable %s in lambda-list" arg))
(t
@@ -2842,7 +2901,8 @@ for symbols generated by the byte compiler itself."
(byte-compile-maxdepth 0)
(byte-compile--lexical-environment lexenv)
(byte-compile-reserved-constants (or reserved-csts 0))
- (byte-compile-output nil))
+ (byte-compile-output nil)
+ (byte-compile-jump-tables nil))
(if (memq byte-optimize '(t source))
(setq form (byte-optimize-form form byte-compile--for-effect)))
(while (and (eq (car-safe form) 'progn) (null (cdr (cdr form))))
@@ -2958,6 +3018,8 @@ for symbols generated by the byte compiler itself."
;; Special macro-expander used during byte-compilation.
(defun byte-compile-macroexpand-declare-function (fn file &rest args)
+ (declare (advertised-calling-convention
+ (fn file &optional arglist fileonly) nil))
(let ((gotargs (and (consp args) (listp (car args))))
(unresolved (assq fn byte-compile-unresolved-functions)))
(when unresolved ; function was called before declaration
@@ -3016,9 +3078,8 @@ for symbols generated by the byte compiler itself."
(pcase (cdr form)
(`(',var . ,_)
(when (assq var byte-compile-lexical-variables)
- (byte-compile-log-warning
- (format-message "%s cannot use lexical var `%s'" fn var)
- nil :error)))))
+ (byte-compile-report-error
+ (format-message "%s cannot use lexical var `%s'" fn var))))))
(when (macroexp--const-symbol-p fn)
(byte-compile-warn "`%s' called as a function" fn))
(when (and (byte-compile-warning-enabled-p 'interactive-only)
@@ -3035,9 +3096,8 @@ for symbols generated by the byte compiler itself."
interactive-only))
(t "."))))
(if (eq (car-safe (symbol-function (car form))) 'macro)
- (byte-compile-log-warning
- (format "Forgot to expand macro %s in %S" (car form) form)
- nil :error))
+ (byte-compile-report-error
+ (format "Forgot to expand macro %s in %S" (car form) form)))
(if (and handler
;; Make sure that function exists.
(and (functionp handler)
@@ -3094,15 +3154,57 @@ for symbols generated by the byte compiler itself."
;; happens to be true for byte-code generated by bytecomp.el without
;; lexical-binding, but it's not true in general, and it's not true for
;; code output by bytecomp.el with lexical-binding.
- (let ((endtag (byte-compile-make-tag)))
+ ;; We also restore the value of `byte-compile-depth' and remove TAG depths
+ ;; accordingly when inlining lapcode containing lap-code, exactly as
+ ;; documented in `byte-compile-cond-jump-table'.
+ (let ((endtag (byte-compile-make-tag))
+ last-jump-tag ;; last TAG we have jumped to
+ last-depth ;; last value of `byte-compile-depth'
+ last-constant ;; value of the last constant encountered
+ last-switch ;; whether the last op encountered was byte-switch
+ switch-tags ;; a list of tags that byte-switch could jump to
+ ;; a list of tags byte-switch will jump to, if the value doesn't
+ ;; match any entry in the hash table
+ switch-default-tags)
(dolist (op lap)
(cond
- ((eq (car op) 'TAG) (byte-compile-out-tag op))
- ((memq (car op) byte-goto-ops) (byte-compile-goto (car op) (cdr op)))
+ ((eq (car op) 'TAG)
+ (when (or (member op switch-tags) (member op switch-default-tags))
+ ;; This TAG is used in a jump table, this means the last goto
+ ;; was to a done/default TAG, and thus it's cddr should be set to nil.
+ (when last-jump-tag
+ (setcdr (cdr last-jump-tag) nil))
+ ;; Also, restore the value of `byte-compile-depth' to what it was
+ ;; before the last goto.
+ (setq byte-compile-depth last-depth
+ last-jump-tag nil))
+ (byte-compile-out-tag op))
+ ((memq (car op) byte-goto-ops)
+ (setq last-depth byte-compile-depth
+ last-jump-tag (cdr op))
+ (byte-compile-goto (car op) (cdr op))
+ (when last-switch
+ ;; The last op was byte-switch, this goto jumps to a "default" TAG
+ ;; (when no value in the jump table is satisfied).
+ (push (cdr op) switch-default-tags)
+ (setcdr (cdr (cdr op)) nil)
+ (setq byte-compile-depth last-depth
+ last-switch nil)))
((eq (car op) 'byte-return)
(byte-compile-discard (- byte-compile-depth end-depth) t)
(byte-compile-goto 'byte-goto endtag))
- (t (byte-compile-out (car op) (cdr op)))))
+ (t
+ (when (eq (car op) 'byte-switch)
+ ;; The last constant is a jump table.
+ (push last-constant byte-compile-jump-tables)
+ (setq last-switch t)
+ ;; Push all TAGs in the jump to switch-tags.
+ (maphash #'(lambda (_k tag)
+ (push tag switch-tags))
+ last-constant))
+ (setq last-constant (and (eq (car op) 'byte-constant) (cadr op)))
+ (setq last-depth byte-compile-depth)
+ (byte-compile-out (car op) (cdr op)))))
(byte-compile-out-tag endtag)))
(defun byte-compile-unfold-bcf (form)
@@ -3114,48 +3216,53 @@ for symbols generated by the byte compiler itself."
(fmax2 (if (numberp fargs) (lsh fargs -7))) ;2*max+rest.
;; (fmin (if (numberp fargs) (logand fargs 127)))
(alen (length (cdr form)))
- (dynbinds ()))
+ (dynbinds ())
+ lap)
(fetch-bytecode fun)
- (mapc 'byte-compile-form (cdr form))
- (unless fmax2
- ;; Old-style byte-code.
- (cl-assert (listp fargs))
- (while fargs
- (pcase (car fargs)
- (`&optional (setq fargs (cdr fargs)))
- (`&rest (setq fmax2 (+ (* 2 (length dynbinds)) 1))
- (push (cadr fargs) dynbinds)
- (setq fargs nil))
- (_ (push (pop fargs) dynbinds))))
- (unless fmax2 (setq fmax2 (* 2 (length dynbinds)))))
- (cond
- ((<= (+ alen alen) fmax2)
- ;; Add missing &optional (or &rest) arguments.
- (dotimes (_ (- (/ (1+ fmax2) 2) alen))
- (byte-compile-push-constant nil)))
- ((zerop (logand fmax2 1))
- (byte-compile-log-warning
- (format "Too many arguments for inlined function %S" form)
- nil :error)
- (byte-compile-discard (- alen (/ fmax2 2))))
- (t
- ;; Turn &rest args into a list.
- (let ((n (- alen (/ (1- fmax2) 2))))
- (cl-assert (> n 0) nil "problem: fmax2=%S alen=%S n=%S" fmax2 alen n)
- (if (< n 5)
- (byte-compile-out
- (aref [byte-list1 byte-list2 byte-list3 byte-list4] (1- n))
- 0)
- (byte-compile-out 'byte-listN n)))))
- (mapc #'byte-compile-dynamic-variable-bind dynbinds)
- (byte-compile-inline-lapcode
- (byte-decompile-bytecode-1 (aref fun 1) (aref fun 2) t)
- (1+ start-depth))
- ;; Unbind dynamic variables.
- (when dynbinds
- (byte-compile-out 'byte-unbind (length dynbinds)))
- (cl-assert (eq byte-compile-depth (1+ start-depth))
- nil "Wrong depth start=%s end=%s" start-depth byte-compile-depth)))
+ (setq lap (byte-decompile-bytecode-1 (aref fun 1) (aref fun 2) t))
+ ;; optimized switch bytecode makes it impossible to guess the correct
+ ;; `byte-compile-depth', which can result in incorrect inlined code.
+ ;; therefore, we do not inline code that uses the `byte-switch'
+ ;; instruction.
+ (if (assq 'byte-switch lap)
+ (byte-compile-normal-call form)
+ (mapc 'byte-compile-form (cdr form))
+ (unless fmax2
+ ;; Old-style byte-code.
+ (cl-assert (listp fargs))
+ (while fargs
+ (pcase (car fargs)
+ (`&optional (setq fargs (cdr fargs)))
+ (`&rest (setq fmax2 (+ (* 2 (length dynbinds)) 1))
+ (push (cadr fargs) dynbinds)
+ (setq fargs nil))
+ (_ (push (pop fargs) dynbinds))))
+ (unless fmax2 (setq fmax2 (* 2 (length dynbinds)))))
+ (cond
+ ((<= (+ alen alen) fmax2)
+ ;; Add missing &optional (or &rest) arguments.
+ (dotimes (_ (- (/ (1+ fmax2) 2) alen))
+ (byte-compile-push-constant nil)))
+ ((zerop (logand fmax2 1))
+ (byte-compile-report-error
+ (format "Too many arguments for inlined function %S" form))
+ (byte-compile-discard (- alen (/ fmax2 2))))
+ (t
+ ;; Turn &rest args into a list.
+ (let ((n (- alen (/ (1- fmax2) 2))))
+ (cl-assert (> n 0) nil "problem: fmax2=%S alen=%S n=%S" fmax2 alen n)
+ (if (< n 5)
+ (byte-compile-out
+ (aref [byte-list1 byte-list2 byte-list3 byte-list4] (1- n))
+ 0)
+ (byte-compile-out 'byte-listN n)))))
+ (mapc #'byte-compile-dynamic-variable-bind dynbinds)
+ (byte-compile-inline-lapcode lap (1+ start-depth))
+ ;; Unbind dynamic variables.
+ (when dynbinds
+ (byte-compile-out 'byte-unbind (length dynbinds)))
+ (cl-assert (eq byte-compile-depth (1+ start-depth))
+ nil "Wrong depth start=%s end=%s" start-depth byte-compile-depth))))
(defun byte-compile-check-variable (var access-type)
"Do various error checks before a use of the variable VAR."
@@ -3746,10 +3853,9 @@ discarding."
(len (length args)))
(if (= (logand len 1) 1)
(progn
- (byte-compile-log-warning
+ (byte-compile-report-error
(format-message
- "missing value for `%S' at end of setq" (car (last args)))
- nil :error)
+ "missing value for `%S' at end of setq" (car (last args))))
(byte-compile-form
`(signal 'wrong-number-of-arguments '(setq ,len))
byte-compile--for-effect))
@@ -3933,37 +4039,164 @@ that suppresses all warnings during execution of BODY."
(byte-compile-out-tag donetag))))
(setq byte-compile--for-effect nil))
+(defun byte-compile-cond-vars (obj1 obj2)
+ ;; We make sure that of OBJ1 and OBJ2, one of them is a symbol,
+ ;; and the other is a constant expression whose value can be
+ ;; compared with `eq' (with `macroexp-const-p').
+ (or
+ (and (symbolp obj1) (macroexp-const-p obj2) (cons obj1 obj2))
+ (and (symbolp obj2) (macroexp-const-p obj1) (cons obj2 obj1))))
+
+(defun byte-compile-cond-jump-table-info (clauses)
+ "If CLAUSES is a `cond' form where:
+The condition for each clause is of the form (TEST VAR VALUE).
+VAR is a variable.
+TEST and VAR are the same throughout all conditions.
+VALUE satisfies `macroexp-const-p'.
+
+Return a list of the form ((TEST . VAR) ((VALUE BODY) ...))"
+ (let ((cases '())
+ (ok t)
+ prev-var prev-test)
+ (and (catch 'break
+ (dolist (clause (cdr clauses) ok)
+ (let* ((condition (car clause))
+ (test (car-safe condition))
+ (vars (when (consp condition)
+ (byte-compile-cond-vars (cadr condition) (cl-caddr condition))))
+ (obj1 (car-safe vars))
+ (obj2 (cdr-safe vars))
+ (body (cdr-safe clause)))
+ (unless prev-var
+ (setq prev-var obj1))
+ (unless prev-test
+ (setq prev-test test))
+ (if (and obj1 (memq test '(eq eql equal))
+ (consp condition)
+ (eq test prev-test)
+ (eq obj1 prev-var)
+ ;; discard duplicate clauses
+ (not (assq obj2 cases)))
+ (push (list (if (consp obj2) (eval obj2) obj2) body) cases)
+ (if (and (macroexp-const-p condition) condition)
+ (progn (push (list 'default (or body `(,condition))) cases)
+ (throw 'break t))
+ (setq ok nil)
+ (throw 'break nil))))))
+ (list (cons prev-test prev-var) (nreverse cases)))))
+
+(defun byte-compile-cond-jump-table (clauses)
+ (let* ((table-info (byte-compile-cond-jump-table-info clauses))
+ (test (caar table-info))
+ (var (cdar table-info))
+ (cases (cadr table-info))
+ jump-table test-obj body tag donetag default-tag default-case)
+ (when (and cases (not (= (length cases) 1)))
+ ;; TODO: Once :linear-search is implemented for `make-hash-table'
+ ;; set it to `t' for cond forms with a small number of cases.
+ (setq jump-table (make-hash-table :test test
+ :purecopy t
+ :size (if (assq 'default cases)
+ (1- (length cases))
+ (length cases)))
+ default-tag (byte-compile-make-tag)
+ donetag (byte-compile-make-tag))
+ ;; The structure of byte-switch code:
+ ;;
+ ;; varref var
+ ;; constant #s(hash-table purecopy t data (val1 (TAG1) val2 (TAG2)))
+ ;; switch
+ ;; goto DEFAULT-TAG
+ ;; TAG1
+ ;; <clause body>
+ ;; goto DONETAG
+ ;; TAG2
+ ;; <clause body>
+ ;; goto DONETAG
+ ;; DEFAULT-TAG
+ ;; <body for `t' clause, if any (else `constant nil')>
+ ;; DONETAG
+
+ (byte-compile-variable-ref var)
+ (byte-compile-push-constant jump-table)
+ (byte-compile-out 'byte-switch)
+
+ ;; When the opcode argument is `byte-goto', `byte-compile-goto' sets
+ ;; `byte-compile-depth' to `nil'. However, we need `byte-compile-depth'
+ ;; to be non-nil for generating tags for all cases. Since
+ ;; `byte-compile-depth' will increase by at most 1 after compiling
+ ;; all of the clause (which is further enforced by cl-assert below)
+ ;; it should be safe to preserve it's value.
+ (let ((byte-compile-depth byte-compile-depth))
+ (byte-compile-goto 'byte-goto default-tag))
+
+ (when (assq 'default cases)
+ (setq default-case (cadr (assq 'default cases))
+ cases (butlast cases 1)))
+
+ (dolist (case cases)
+ (setq tag (byte-compile-make-tag)
+ test-obj (nth 0 case)
+ body (nth 1 case))
+ (byte-compile-out-tag tag)
+ (puthash test-obj tag jump-table)
+
+ (let ((byte-compile-depth byte-compile-depth)
+ (init-depth byte-compile-depth))
+ ;; Since `byte-compile-body' might increase `byte-compile-depth'
+ ;; by 1, not preserving it's value will cause it to potentially
+ ;; increase by one for every clause body compiled, causing
+ ;; depth/tag conflicts or violating asserts down the road.
+ ;; To make sure `byte-compile-body' itself doesn't violate this,
+ ;; we use `cl-assert'.
+ (if (null body)
+ (byte-compile-form t byte-compile--for-effect)
+ (byte-compile-body body byte-compile--for-effect))
+ (cl-assert (or (= byte-compile-depth init-depth)
+ (= byte-compile-depth (1+ init-depth))))
+ (byte-compile-goto 'byte-goto donetag)
+ (setcdr (cdr donetag) nil)))
+
+ (byte-compile-out-tag default-tag)
+ (if default-case
+ (byte-compile-body-do-effect default-case)
+ (byte-compile-constant nil))
+ (byte-compile-out-tag donetag)
+ (push jump-table byte-compile-jump-tables))))
+
(defun byte-compile-cond (clauses)
- (let ((donetag (byte-compile-make-tag))
- nexttag clause)
- (while (setq clauses (cdr clauses))
- (setq clause (car clauses))
- (cond ((or (eq (car clause) t)
- (and (eq (car-safe (car clause)) 'quote)
- (car-safe (cdr-safe (car clause)))))
- ;; Unconditional clause
- (setq clause (cons t clause)
- clauses nil))
- ((cdr clauses)
- (byte-compile-form (car clause))
- (if (null (cdr clause))
- ;; First clause is a singleton.
- (byte-compile-goto-if t byte-compile--for-effect donetag)
- (setq nexttag (byte-compile-make-tag))
- (byte-compile-goto 'byte-goto-if-nil nexttag)
- (byte-compile-maybe-guarded (car clause)
- (byte-compile-body (cdr clause) byte-compile--for-effect))
- (byte-compile-goto 'byte-goto donetag)
- (byte-compile-out-tag nexttag)))))
- ;; Last clause
- (let ((guard (car clause)))
- (and (cdr clause) (not (eq guard t))
- (progn (byte-compile-form guard)
- (byte-compile-goto-if nil byte-compile--for-effect donetag)
- (setq clause (cdr clause))))
- (byte-compile-maybe-guarded guard
- (byte-compile-body-do-effect clause)))
- (byte-compile-out-tag donetag)))
+ (or (and byte-compile-cond-use-jump-table
+ (byte-compile-cond-jump-table clauses))
+ (let ((donetag (byte-compile-make-tag))
+ nexttag clause)
+ (while (setq clauses (cdr clauses))
+ (setq clause (car clauses))
+ (cond ((or (eq (car clause) t)
+ (and (eq (car-safe (car clause)) 'quote)
+ (car-safe (cdr-safe (car clause)))))
+ ;; Unconditional clause
+ (setq clause (cons t clause)
+ clauses nil))
+ ((cdr clauses)
+ (byte-compile-form (car clause))
+ (if (null (cdr clause))
+ ;; First clause is a singleton.
+ (byte-compile-goto-if t byte-compile--for-effect donetag)
+ (setq nexttag (byte-compile-make-tag))
+ (byte-compile-goto 'byte-goto-if-nil nexttag)
+ (byte-compile-maybe-guarded (car clause)
+ (byte-compile-body (cdr clause) byte-compile--for-effect))
+ (byte-compile-goto 'byte-goto donetag)
+ (byte-compile-out-tag nexttag)))))
+ ;; Last clause
+ (let ((guard (car clause)))
+ (and (cdr clause) (not (eq guard t))
+ (progn (byte-compile-form guard)
+ (byte-compile-goto-if nil byte-compile--for-effect donetag)
+ (setq clause (cdr clause))))
+ (byte-compile-maybe-guarded guard
+ (byte-compile-body-do-effect clause)))
+ (byte-compile-out-tag donetag))))
(defun byte-compile-and (form)
(let ((failtag (byte-compile-make-tag))
@@ -4019,8 +4252,8 @@ that suppresses all warnings during execution of BODY."
(progn
(mapc 'byte-compile-form (cdr form))
(byte-compile-out 'byte-call (length (cdr (cdr form)))))
- (byte-compile-log-warning
- (format-message "`funcall' called with no arguments") nil :error)
+ (byte-compile-report-error
+ (format-message "`funcall' called with no arguments"))
(byte-compile-form '(signal 'wrong-number-of-arguments '(funcall 0))
byte-compile--for-effect)))
@@ -4510,7 +4743,7 @@ binding slots have been popped."
(and byte-compile-depth
(not (= (cdr (cdr tag)) byte-compile-depth))
(error "Compiler bug: depth conflict at tag %d" (car (cdr tag))))
- (setq byte-compile-depth (cdr (cdr tag))))
+ (setq byte-compile-depth (cdr (cdr tag))))
(setcdr (cdr tag) byte-compile-depth)))
(defun byte-compile-goto (opcode tag)
@@ -4732,6 +4965,10 @@ already up-to-date."
(defvar command-line-args-left) ;Avoid 'free variable' warning
(if (not noninteractive)
(error "`batch-byte-compile' is to be used only with -batch"))
+ ;; Better crash loudly than attempting to recover from undefined
+ ;; behavior.
+ (setq attempt-stack-overflow-recovery nil
+ attempt-orderly-shutdown-on-fatal-signal nil)
(let ((error nil))
(while command-line-args-left
(if (file-directory-p (expand-file-name (car command-line-args-left)))
@@ -4824,6 +5061,10 @@ and corresponding effects."
(defvar command-line-args-left) ;Avoid 'free variable' warning
(if (not noninteractive)
(error "batch-byte-recompile-directory is to be used only with -batch"))
+ ;; Better crash loudly than attempting to recover from undefined
+ ;; behavior.
+ (setq attempt-stack-overflow-recovery nil
+ attempt-orderly-shutdown-on-fatal-signal nil)
(or command-line-args-left
(setq command-line-args-left '(".")))
(while command-line-args-left
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 3d6132c9aa6..4507af7a59b 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -253,6 +253,32 @@ Returns a form where all lambdas don't have any free variables."
`(internal-make-closure
,args ,envector ,docstring . ,body-new)))))
+(defun cconv--remap-llv (new-env var closedsym)
+ ;; In a case such as:
+ ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
+ ;; A naive lambda-lifting would return
+ ;; (let* ((fun (lambda (y x) (+ x y))) (y 1)) (funcall fun y 1))
+ ;; Where the external `y' is mistakenly captured by the inner one.
+ ;; So when we detect that case, we rewrite it to:
+ ;; (let* ((closed-y y) (fun (lambda (y x) (+ x y))) (y 1))
+ ;; (funcall fun closed-y 1))
+ ;; We do that even if there's no `funcall' that uses `fun' in the scope
+ ;; where `y' is shadowed by another variable because, to treat
+ ;; this case better, we'd need to traverse the tree one more time to
+ ;; collect this data, and I think that it's not worth it.
+ (mapcar (lambda (mapping)
+ (if (not (eq (cadr mapping) 'apply-partially))
+ mapping
+ (cl-assert (eq (car mapping) (nth 2 mapping)))
+ `(,(car mapping)
+ apply-partially
+ ,(car mapping)
+ ,@(mapcar (lambda (arg)
+ (if (eq var arg)
+ closedsym arg))
+ (nthcdr 3 mapping)))))
+ new-env))
+
(defun cconv-convert (form env extend)
;; This function actually rewrites the tree.
"Return FORM with all its lambdas changed so they are closed.
@@ -299,9 +325,9 @@ places where they originally did not directly appear."
(var (if (not (consp binder))
(prog1 binder (setq binder (list binder)))
(when (cddr binder)
- (byte-compile-log-warning
- (format-message "Malformed `%S' binding: %S"
- letsym binder)))
+ (byte-compile-warn
+ "Malformed `%S' binding: %S"
+ letsym binder))
(setq value (cadr binder))
(car binder)))
(new-val
@@ -350,34 +376,13 @@ places where they originally did not directly appear."
(if (assq var new-env) (push `(,var) new-env))
(cconv-convert value env extend)))))
- ;; The piece of code below letbinds free variables of a λ-lifted
- ;; function if they are redefined in this let, example:
- ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
- ;; Here we can not pass y as parameter because it is redefined.
- ;; So we add a (closed-y y) declaration. We do that even if the
- ;; function is not used inside this let(*). The reason why we
- ;; ignore this case is that we can't "look forward" to see if the
- ;; function is called there or not. To treat this case better we'd
- ;; need to traverse the tree one more time to collect this data, and
- ;; I think that it's not worth it.
- (when (memq var new-extend)
- (let ((closedsym
- (make-symbol (concat "closed-" (symbol-name var)))))
- (setq new-env
- (mapcar (lambda (mapping)
- (if (not (eq (cadr mapping) 'apply-partially))
- mapping
- (cl-assert (eq (car mapping) (nth 2 mapping)))
- `(,(car mapping)
- apply-partially
- ,(car mapping)
- ,@(mapcar (lambda (arg)
- (if (eq var arg)
- closedsym arg))
- (nthcdr 3 mapping)))))
- new-env))
- (setq new-extend (remq var new-extend))
- (push closedsym new-extend)
+ (when (and (eq letsym 'let*) (memq var new-extend))
+ ;; One of the lambda-lifted vars is shadowed, so add
+ ;; a reference to the outside binding and arrange to use
+ ;; that reference.
+ (let ((closedsym (make-symbol (format "closed-%s" var))))
+ (setq new-env (cconv--remap-llv new-env var closedsym))
+ (setq new-extend (cons closedsym (remq var new-extend)))
(push `(,closedsym ,var) binders-new)))
;; We push the element after redefined free variables are
@@ -390,6 +395,21 @@ places where they originally did not directly appear."
(setq extend new-extend))
)) ; end of dolist over binders
+ (when (not (eq letsym 'let*))
+ ;; We can't do the cconv--remap-llv at the same place for let and
+ ;; let* because in the case of `let', the shadowing may occur
+ ;; before we know that the var will be in `new-extend' (bug#24171).
+ (dolist (binder binders-new)
+ (when (memq (car-safe binder) new-extend)
+ ;; One of the lambda-lifted vars is shadowed, so add
+ ;; a reference to the outside binding and arrange to use
+ ;; that reference.
+ (let* ((var (car-safe binder))
+ (closedsym (make-symbol (format "closed-%s" var))))
+ (setq new-env (cconv--remap-llv new-env var closedsym))
+ (setq new-extend (cons closedsym (remq var new-extend)))
+ (push `(,closedsym ,var) binders-new)))))
+
`(,letsym ,(nreverse binders-new)
. ,(mapcar (lambda (form)
(cconv-convert
@@ -548,8 +568,8 @@ FORM is the parent form that binds this var."
(`(,_ nil nil nil nil) nil)
(`((,(and var (guard (eq ?_ (aref (symbol-name var) 0)))) . ,_)
,_ ,_ ,_ ,_)
- (byte-compile-log-warning
- (format-message "%s `%S' not left unused" varkind var))))
+ (byte-compile-warn
+ "%s `%S' not left unused" varkind var)))
(pcase vardata
(`((,var . ,_) nil ,_ ,_ nil)
;; FIXME: This gives warnings in the wrong order, with imprecise line
@@ -561,8 +581,8 @@ FORM is the parent form that binds this var."
(eq ?_ (aref (symbol-name var) 0))
;; As a special exception, ignore "ignore".
(eq var 'ignored))
- (byte-compile-log-warning (format-message "Unused lexical %s `%S'"
- varkind var))))
+ (byte-compile-warn "Unused lexical %s `%S'"
+ varkind var)))
;; If it's unused, there's no point converting it into a cons-cell, even if
;; it's captured and mutated.
(`(,binder ,_ t t ,_)
@@ -586,9 +606,9 @@ FORM is the parent form that binds this var."
(dolist (arg args)
(cond
((byte-compile-not-lexical-var-p arg)
- (byte-compile-log-warning
- (format "Lexical argument shadows the dynamic variable %S"
- arg)))
+ (byte-compile-warn
+ "Lexical argument shadows the dynamic variable %S"
+ arg))
((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ...
(t (let ((varstruct (list arg nil nil nil nil)))
(cl-pushnew arg byte-compile-lexical-variables)
@@ -670,9 +690,8 @@ and updates the data stored in ENV."
(setq forms (cddr forms))))
(`((lambda . ,_) . ,_) ; First element is lambda expression.
- (byte-compile-log-warning
- (format "Use of deprecated ((lambda %s ...) ...) form" (nth 1 (car form)))
- t :warning)
+ (byte-compile-warn
+ "Use of deprecated ((lambda %s ...) ...) form" (nth 1 (car form)))
(dolist (exp `((function ,(car form)) . ,(cdr form)))
(cconv-analyze-form exp env)))
@@ -681,8 +700,8 @@ and updates the data stored in ENV."
(dolist (form forms) (cconv-analyze-form form env))))
;; ((and `(quote ,v . ,_) (guard (assq v env)))
- ;; (byte-compile-log-warning
- ;; (format-message "Possible confusion variable/symbol for `%S'" v)))
+ ;; (byte-compile-warn
+ ;; "Possible confusion variable/symbol for `%S'" v))
(`(quote . ,_) nil) ; quote form
(`(function . ,_) nil) ; same as quote
@@ -699,8 +718,8 @@ and updates the data stored in ENV."
(`(condition-case ,var ,protected-form . ,handlers)
(cconv-analyze-form protected-form env)
(when (and var (symbolp var) (byte-compile-not-lexical-var-p var))
- (byte-compile-log-warning
- (format "Lexical variable shadows the dynamic variable %S" var)))
+ (byte-compile-warn
+ "Lexical variable shadows the dynamic variable %S" var))
(let* ((varstruct (list var nil nil nil nil)))
(if var (push varstruct env))
(dolist (handler handlers)
diff --git a/lisp/emacs-lisp/chart.el b/lisp/emacs-lisp/chart.el
index 1538728475c..dc108f956c2 100644
--- a/lisp/emacs-lisp/chart.el
+++ b/lisp/emacs-lisp/chart.el
@@ -60,6 +60,7 @@
;; with all the bitmaps you want to use.
(require 'eieio)
+(eval-when-compile (require 'cl-lib))
(eval-when-compile (require 'cl-generic))
;;; Code:
@@ -118,7 +119,7 @@ Useful if new Emacs is used on B&W display.")
List is limited currently, which is ok since you really can't display
too much in text characters anyways.")
-(define-derived-mode chart-mode fundamental-mode "CHART"
+(define-derived-mode chart-mode special-mode "Chart"
"Define a mode in Emacs for displaying a chart."
(buffer-disable-undo)
(set (make-local-variable 'font-lock-global-modes) nil)
@@ -205,22 +206,23 @@ Make sure the width/height is correct."
(cl-defmethod chart-draw ((c chart) &optional buff)
"Start drawing a chart object C in optional BUFF.
Erases current contents of buffer."
- (save-excursion
- (if buff (set-buffer buff))
- (erase-buffer)
- (insert (make-string 100 ?\n))
- ;; Start by displaying the axis
- (chart-draw-axis c)
- ;; Display title
- (chart-draw-title c)
- ;; Display data
- (message "Rendering chart...")
- (sit-for 0)
- (chart-draw-data c)
- ;; Display key
- ; (chart-draw-key c)
- (message "Rendering chart...done")
- ))
+ (with-silent-modifications
+ (save-excursion
+ (if buff (set-buffer buff))
+ (erase-buffer)
+ (insert (make-string (window-height (selected-window)) ?\n))
+ ;; Start by displaying the axis
+ (chart-draw-axis c)
+ ;; Display title
+ (chart-draw-title c)
+ ;; Display data
+ (message "Rendering chart...")
+ (sit-for 0)
+ (chart-draw-data c)
+ ;; Display key
+ ; (chart-draw-key c)
+ (message "Rendering chart...done")
+ )))
(cl-defmethod chart-draw-title ((c chart))
"Draw a title upon the chart.
@@ -434,11 +436,10 @@ or is created with the bounds of SEQ."
(setq axis (make-instance 'chart-axis-range
:name (oref seq name)
:chart c)))
- (while l
- (if (< (car l) (car range)) (setcar range (car l)))
- (if (> (car l) (cdr range)) (setcdr range (car l)))
- (setq l (cdr l)))
- (oset axis bounds range)))
+ (dolist (x l)
+ (if (< x (car range)) (setcar range x))
+ (if (> x (cdr range)) (setcdr range x)))
+ (oset axis bounds range)))
(if (eq axis-label 'x-axis) (oset axis loweredge nil))
(eieio-oset c axis-label axis)
))
@@ -449,11 +450,10 @@ or is created with the bounds of SEQ."
(cl-defmethod chart-trim ((c chart) max)
"Trim all sequences in chart C to be at most MAX elements long."
(let ((s (oref c sequences)))
- (while s
- (let ((sl (oref (car s) data)))
+ (dolist (x s)
+ (let ((sl (oref x data)))
(if (> (length sl) max)
- (setcdr (nthcdr (1- max) sl) nil)))
- (setq s (cdr s))))
+ (setcdr (nthcdr (1- max) sl) nil)))))
)
(cl-defmethod chart-sort ((c chart) pred)
@@ -614,27 +614,20 @@ SORT-PRED if desired."
(defun chart-file-count (dir)
"Draw a chart displaying the number of different file extensions in DIR."
(interactive "DDirectory: ")
- (if (not (string-match "/$" dir))
- (setq dir (concat dir "/")))
(message "Collecting statistics...")
(let ((flst (directory-files dir nil nil t))
(extlst (list "<dir>"))
(cntlst (list 0)))
- (while flst
- (let* ((j (string-match "[^\\.]\\(\\.[a-zA-Z]+\\|~\\|#\\)$" (car flst)))
- (s (if (file-accessible-directory-p (concat dir (car flst)))
- "<dir>"
- (if j
- (substring (car flst) (match-beginning 1) (match-end 1))
- nil)))
+ (dolist (f flst)
+ (let* ((x (file-name-extension f))
+ (s (if (file-accessible-directory-p (expand-file-name f dir))
+ "<dir>" x))
(m (member s extlst)))
- (if (not s) nil
+ (unless (null s)
(if m
- (let ((cell (nthcdr (- (length extlst) (length m)) cntlst)))
- (setcar cell (1+ (car cell))))
+ (cl-incf (car (nthcdr (- (length extlst) (length m)) cntlst)))
(setq extlst (cons s extlst)
- cntlst (cons 1 cntlst)))))
- (setq flst (cdr flst)))
+ cntlst (cons 1 cntlst))))))
;; Let's create the chart!
(chart-bar-quickie 'vertical "Files Extension Distribution"
extlst "File Extensions"
diff --git a/lisp/emacs-lisp/check-declare.el b/lisp/emacs-lisp/check-declare.el
index 8665b9dc599..c46426cd366 100644
--- a/lisp/emacs-lisp/check-declare.el
+++ b/lisp/emacs-lisp/check-declare.el
@@ -43,7 +43,7 @@
"Name of buffer used to display any `check-declare' warnings.")
(defun check-declare-locate (file basefile)
- "Return the full path of FILE.
+ "Return the relative name of FILE.
Expands files with a \".c\" or \".m\" extension relative to the Emacs
\"src/\" directory. Otherwise, `locate-library' searches for FILE.
If that fails, expands FILE relative to BASEFILE's directory part.
@@ -70,6 +70,7 @@ the result."
(string-match "\\.el\\'" tfile))
tfile
(concat tfile ".el")))))
+ (setq file (file-relative-name file))
(if ext (concat "ext:" file)
file)))
@@ -80,49 +81,40 @@ where only the first two elements need be present. This claims that FNFILE
defines FN, with ARGLIST. FILEONLY non-nil means only check that FNFILE
exists, not that it defines FN. This is for function definitions that we
don't know how to recognize (e.g. some macros)."
- (let ((m (format "Scanning %s..." file))
- alist form len fn fnfile arglist fileonly)
- (message "%s" m)
+ (let (alist)
(with-temp-buffer
(insert-file-contents file)
;; FIXME we could theoretically be inside a string.
(while (re-search-forward "^[ \t]*\\((declare-function\\)[ \t\n]" nil t)
- (goto-char (match-beginning 1))
- (if (and (setq form (ignore-errors (read (current-buffer))))
+ (let ((pos (match-beginning 1)))
+ (goto-char pos)
+ (let ((form (ignore-errors (read (current-buffer))))
+ len fn formfile fnfile arglist fileonly)
+ (if (and
;; Exclude element of byte-compile-initial-macro-environment.
(or (listp (cdr form)) (setq form nil))
(> (setq len (length form)) 2)
(< len 6)
+ (setq formfile (nth 2 form))
(symbolp (setq fn (cadr form)))
(setq fn (symbol-name fn)) ; later we use as a search string
- (stringp (setq fnfile (nth 2 form)))
- (setq fnfile (check-declare-locate fnfile
- (expand-file-name file)))
+ (stringp formfile)
+ (setq fnfile (check-declare-locate formfile file))
;; Use t to distinguish unspecified arglist from empty one.
(or (eq t (setq arglist (if (> len 3)
(nth 3 form)
t)))
(listp arglist))
(symbolp (setq fileonly (nth 4 form))))
- (setq alist (cons (list fnfile fn arglist fileonly) alist))
- ;; FIXME make this more noticeable.
- (if form (message "Malformed declaration for `%s'" (cadr form))))))
- (message "%sdone" m)
+ (setq alist (cons (list fnfile fn arglist fileonly) alist))
+ (when form
+ (check-declare-warn file (or fn "unknown function")
+ (if (stringp formfile) formfile
+ "unknown file")
+ "Malformed declaration"
+ (line-number-at-pos pos))))))))
alist))
-(defun check-declare-errmsg (errlist &optional full)
- "Return a string with the number of errors in ERRLIST, if any.
-Normally just counts the number of elements in ERRLIST.
-With optional argument FULL, sums the number of elements in each element."
- (if errlist
- (let ((l (length errlist)))
- (when full
- (setq l 0)
- (dolist (e errlist)
- (setq l (+ l (1- (length e))))))
- (format "%d problem%s found" l (if (= l 1) "" "s")))
- "OK"))
-
(autoload 'byte-compile-arglist-signature "bytecomp")
(defgroup check-declare nil
@@ -144,11 +136,9 @@ to only check that FNFILE exists, not that it actually defines FN.
Returns nil if all claims are found to be true, otherwise a list
of errors with elements of the form \(FILE FN TYPE), where TYPE
is a string giving details of the error."
- (let ((m (format "Checking %s..." fnfile))
- (cflag (member (file-name-extension fnfile) '("c" "m")))
+ (let ((cflag (member (file-name-extension fnfile) '("c" "m")))
(ext (string-match "^ext:" fnfile))
re fn sig siglist arglist type errlist minargs maxargs)
- (message "%s" m)
(if ext
(setq fnfile (substring fnfile 4)))
(if (file-regular-p fnfile)
@@ -216,7 +206,8 @@ fset\\|\\(?:cl-\\)?defmethod\\)\\>" type)
(setq arglist (nth 2 e)
type
(if (not re)
- "file not found"
+ (when (or check-declare-ext-errors (not ext))
+ "file not found")
(if (not (setq sig (assoc (cadr e) siglist)))
(unless (nth 3 e) ; fileonly
"function not found")
@@ -235,13 +226,6 @@ fset\\|\\(?:cl-\\)?defmethod\\)\\>" type)
"arglist mismatch")))))
(when type
(setq errlist (cons (list (car e) (cadr e) type) errlist))))
- (message "%s%s" m
- (if (or re (or check-declare-ext-errors
- (not ext)))
- (check-declare-errmsg errlist)
- (progn
- (setq errlist nil)
- "skipping external file")))
errlist))
(defun check-declare-sort (alist)
@@ -258,30 +242,27 @@ Returned list has elements FNFILE (FILE ...)."
(setq sort (cons (list fnfile (cons file rest)) sort)))))
sort))
-(defun check-declare-warn (file fn fnfile type)
+(defun check-declare-warn (file fn fnfile type &optional line)
"Warn that FILE made a false claim about FN in FNFILE.
-TYPE is a string giving the nature of the error. Warning is displayed in
-`check-declare-warning-buffer'."
+TYPE is a string giving the nature of the error.
+Optional LINE is the claim's line number; otherwise, search for the claim.
+Display warning in `check-declare-warning-buffer'."
(let ((warning-prefix-function
(lambda (level entry)
- (let ((line 0)
- (col 0))
- (insert
- (with-current-buffer (find-file-noselect file)
- (goto-char (point-min))
- (when (re-search-forward
- (format "(declare-function[ \t\n]+%s" fn) nil t)
- (goto-char (match-beginning 0))
- (setq line (line-number-at-pos))
- (setq col (1+ (current-column))))
- (format "%s:%d:%d:"
- (file-name-nondirectory file)
- line col))))
+ (insert (format "%s:%d:" (file-relative-name file) (or line 0)))
entry))
(warning-fill-prefix " "))
+ (unless line
+ (with-current-buffer (find-file-noselect file)
+ (goto-char (point-min))
+ (when (and (not line)
+ (re-search-forward
+ (format "(declare-function[ \t\n]+%s" fn) nil t))
+ (goto-char (match-beginning 0))
+ (setq line (line-number-at-pos)))))
(display-warning 'check-declare
(format-message "said `%s' was defined in %s: %s"
- fn (file-name-nondirectory fnfile) type)
+ fn (file-relative-name fnfile) type)
nil check-declare-warning-buffer)))
(declare-function compilation-forget-errors "compile" ())
@@ -289,7 +270,18 @@ TYPE is a string giving the nature of the error. Warning is displayed in
(defun check-declare-files (&rest files)
"Check veracity of all `declare-function' statements in FILES.
Return a list of any errors found."
- (let (alist err errlist)
+ (if (get-buffer check-declare-warning-buffer)
+ (kill-buffer check-declare-warning-buffer))
+ (let ((buf (get-buffer-create check-declare-warning-buffer))
+ alist err errlist)
+ (with-current-buffer buf
+ (unless (derived-mode-p 'compilation-mode)
+ (compilation-mode))
+ (setq mode-line-process
+ '(:propertize ":run" face compilation-mode-line-run))
+ (let ((inhibit-read-only t))
+ (insert "\f\n"))
+ (compilation-forget-errors))
(dolist (file files)
(setq alist (cons (cons file (check-declare-scan file)) alist)))
;; Sort so that things are ordered by the files supposed to
@@ -298,19 +290,15 @@ Return a list of any errors found."
(if (setq err (check-declare-verify (car e) (cdr e)))
(setq errlist (cons (cons (car e) err) errlist))))
(setq errlist (nreverse errlist))
- (if (get-buffer check-declare-warning-buffer)
- (kill-buffer check-declare-warning-buffer))
- (with-current-buffer (get-buffer-create check-declare-warning-buffer)
- (unless (derived-mode-p 'compilation-mode)
- (compilation-mode))
- (let ((inhibit-read-only t))
- (insert "\f\n"))
- (compilation-forget-errors))
;; Sort back again so that errors are ordered by the files
;; containing the declare-function statements.
(dolist (e (check-declare-sort errlist))
(dolist (f (cdr e))
(check-declare-warn (car e) (cadr f) (car f) (nth 2 f))))
+ (with-current-buffer buf
+ (setq mode-line-process
+ '(:propertize ":exit" face compilation-mode-line-run))
+ (force-mode-line-update))
errlist))
;;;###autoload
@@ -320,34 +308,22 @@ See `check-declare-directory' for more information."
(interactive "fFile to check: ")
(or (file-exists-p file)
(error "File `%s' not found" file))
- (let ((m (format "Checking %s..." file))
- errlist)
- (message "%s" m)
- (setq errlist (check-declare-files file))
- (message "%s%s" m (check-declare-errmsg errlist))
- errlist))
+ (check-declare-files file))
;;;###autoload
(defun check-declare-directory (root)
"Check veracity of all `declare-function' statements under directory ROOT.
Returns non-nil if any false statements are found."
(interactive "DDirectory to check: ")
- (or (file-directory-p (setq root (expand-file-name root)))
+ (setq root (directory-file-name (file-relative-name root)))
+ (or (file-directory-p root)
(error "Directory `%s' not found" root))
- (let ((m "Checking `declare-function' statements...")
- (m2 "Finding files with declarations...")
- errlist files)
- (message "%s" m)
- (message "%s" m2)
- (setq files (process-lines find-program root
- "-name" "*.el"
- "-exec" grep-program
- "-l" "^[ \t]*(declare-function" "{}" ";"))
- (message "%s%d found" m2 (length files))
+ (let ((files (process-lines find-program root
+ "-name" "*.el"
+ "-exec" grep-program
+ "-l" "^[ \t]*(declare-function" "{}" "+")))
(when files
- (setq errlist (apply 'check-declare-files files))
- (message "%s%s" m (check-declare-errmsg errlist t))
- errlist)))
+ (apply #'check-declare-files files))))
(provide 'check-declare)
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 3e782e0a809..1d6fdfa4e87 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -294,12 +294,6 @@ problem discovered. This is useful for adding additional checks.")
(defvar checkdoc-diagnostic-buffer "*Style Warnings*"
"Name of warning message buffer.")
-(defvar checkdoc-defun-regexp
- "^(def\\(un\\|var\\|custom\\|macro\\|const\\|subst\\|advice\\)\
-\\s-+\\(\\(\\sw\\|\\s_\\)+\\)[ \t\n]+"
- "Regular expression used to identify a defun.
-A search leaves the cursor in front of the parameter list.")
-
(defcustom checkdoc-verb-check-experimental-flag t
"Non-nil means to attempt to check the voice of the doc string.
This check keys off some words which are commonly misused. See the
@@ -609,7 +603,7 @@ style."
(checkdoc-overlay-put cdo 'face 'highlight)
;; Make sure the whole doc string is visible if possible.
(sit-for 0)
- (if (and (looking-at "\"")
+ (if (and (= (following-char) ?\")
(not (pos-visible-in-window-p
(save-excursion (forward-sexp 1) (point))
(selected-window))))
@@ -749,9 +743,9 @@ buffer, otherwise searching starts at START-HERE."
(while (checkdoc-next-docstring)
(message "Searching for doc string spell error...%d%%"
(floor (* 100.0 (point)) (point-max)))
- (if (looking-at "\"")
- (checkdoc-ispell-docstring-engine
- (save-excursion (forward-sexp 1) (point-marker)))))
+ (when (= (following-char) ?\")
+ (checkdoc-ispell-docstring-engine
+ (save-excursion (forward-sexp 1) (point-marker)))))
(message "Checkdoc: Done."))))
(defun checkdoc-message-interactive-ispell-loop (start-here)
@@ -769,7 +763,7 @@ buffer, otherwise searching starts at START-HERE."
(while (checkdoc-message-text-next-string (point-max))
(message "Searching for message string spell error...%d%%"
(floor (* 100.0 (point)) (point-max)))
- (if (looking-at "\"")
+ (if (= (following-char) ?\")
(checkdoc-ispell-docstring-engine
(save-excursion (forward-sexp 1) (point-marker)))))
(message "Checkdoc: Done."))))
@@ -938,13 +932,31 @@ is the starting location. If this is nil, `point-min' is used instead."
(defun checkdoc-next-docstring ()
"Move to the next doc string after point, and return t.
Return nil if there are no more doc strings."
- (if (not (re-search-forward checkdoc-defun-regexp nil t))
- nil
- ;; search drops us after the identifier. The next sexp is either
- ;; the argument list or the value of the variable. skip it.
- (forward-sexp 1)
- (skip-chars-forward " \n\t")
- t))
+ (let (found)
+ (while (and (not (setq found (checkdoc--next-docstring)))
+ (beginning-of-defun -1)))
+ found))
+
+(defun checkdoc--next-docstring ()
+ "When looking at a definition with a doc string, find it.
+Move to the next doc string after point, and return t. When not
+looking at a definition containing a doc string, return nil and
+don't move point."
+ (pcase (save-excursion (condition-case nil
+ (read (current-buffer))
+ ;; Conservatively skip syntax errors.
+ (invalid-read-syntax)))
+ (`(,(or 'defun 'defvar 'defcustom 'defmacro 'defconst 'defsubst 'defadvice)
+ ,(pred symbolp)
+ ;; Require an initializer, i.e. ignore single-argument `defvar'
+ ;; forms, which never have a doc string.
+ ,_ . ,_)
+ (down-list)
+ ;; Skip over function or macro name, symbol to be defined, and
+ ;; initializer or argument list.
+ (forward-sexp 3)
+ (skip-chars-forward " \n\t")
+ t)))
;;;###autoload
(defun checkdoc-comments (&optional take-notes)
@@ -1027,21 +1039,12 @@ space at the end of each line."
(interactive)
(save-excursion
(beginning-of-defun)
- (if (not (looking-at checkdoc-defun-regexp))
- ;; I found this more annoying than useful.
- ;;(if (not no-error)
- ;; (message "Cannot check this sexp's doc string."))
- nil
- ;; search drops us after the identifier. The next sexp is either
- ;; the argument list or the value of the variable. skip it.
- (goto-char (match-end 0))
- (forward-sexp 1)
- (skip-chars-forward " \n\t")
+ (when (checkdoc--next-docstring)
(let* ((checkdoc-spellcheck-documentation-flag
- (car (memq checkdoc-spellcheck-documentation-flag
+ (car (memq checkdoc-spellcheck-documentation-flag
'(defun t))))
- (beg (save-excursion (beginning-of-defun) (point)))
- (end (save-excursion (end-of-defun) (point))))
+ (beg (save-excursion (beginning-of-defun) (point)))
+ (end (save-excursion (end-of-defun) (point))))
(dolist (fun (list #'checkdoc-this-string-valid
(lambda () (checkdoc-message-text-search beg end))
(lambda () (checkdoc-rogue-space-check-engine beg end))))
@@ -1049,8 +1052,8 @@ space at the end of each line."
(if msg (if no-error
(message "%s" (checkdoc-error-text msg))
(user-error "%s" (checkdoc-error-text msg))))))
- (if (called-interactively-p 'interactive)
- (message "Checkdoc: done."))))))
+ (if (called-interactively-p 'interactive)
+ (message "Checkdoc: done."))))))
;;; Ispell interface for forcing a spell check
;;
@@ -1062,7 +1065,7 @@ Calls `checkdoc' with spell-checking turned on.
Prefix argument is the same as for `checkdoc'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc nil current-prefix-arg)))
+ (call-interactively #'checkdoc)))
;;;###autoload
(defun checkdoc-ispell-current-buffer ()
@@ -1071,7 +1074,7 @@ Calls `checkdoc-current-buffer' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-current-buffer'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-current-buffer nil current-prefix-arg)))
+ (call-interactively #'checkdoc-current-buffer)))
;;;###autoload
(defun checkdoc-ispell-interactive ()
@@ -1080,7 +1083,7 @@ Calls `checkdoc-interactive' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-interactive'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-interactive nil current-prefix-arg)))
+ (call-interactively #'checkdoc-interactive)))
;;;###autoload
(defun checkdoc-ispell-message-interactive ()
@@ -1099,7 +1102,7 @@ Calls `checkdoc-message-text' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-message-text'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-message-text nil current-prefix-arg)))
+ (call-interactively #'checkdoc-message-text)))
;;;###autoload
(defun checkdoc-ispell-start ()
@@ -1108,7 +1111,7 @@ Calls `checkdoc-start' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-start'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-start nil current-prefix-arg)))
+ (call-interactively #'checkdoc-start)))
;;;###autoload
(defun checkdoc-ispell-continue ()
@@ -1117,7 +1120,7 @@ Calls `checkdoc-continue' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-continue'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-continue nil current-prefix-arg)))
+ (call-interactively #'checkdoc-continue)))
;;;###autoload
(defun checkdoc-ispell-comments ()
@@ -1126,7 +1129,7 @@ Calls `checkdoc-comments' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-comments'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-comments nil current-prefix-arg)))
+ (call-interactively #'checkdoc-comments)))
;;;###autoload
(defun checkdoc-ispell-defun ()
@@ -1135,7 +1138,7 @@ Calls `checkdoc-defun' with spell-checking turned on.
Prefix argument is the same as for `checkdoc-defun'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively #'checkdoc-defun nil current-prefix-arg)))
+ (call-interactively #'checkdoc-defun)))
;;; Error Management
;;
@@ -1378,7 +1381,7 @@ See the style guide in the Emacs Lisp manual for more details."
"All variables and subroutines might as well have a \
documentation string")
(point) (+ (point) 1) t)))))
- (if (and (not err) (looking-at "\""))
+ (if (and (not err) (= (following-char) ?\"))
(with-syntax-table checkdoc-syntax-table
(checkdoc-this-string-valid-engine fp))
err)))
@@ -1392,7 +1395,7 @@ regexp short cuts work. FP is the function defun information."
;; we won't accidentally lose our place. This could cause
;; end-of doc string whitespace to also delete the " char.
(s (point))
- (e (if (looking-at "\"")
+ (e (if (= (following-char) ?\")
(save-excursion (forward-sexp 1) (point-marker))
(point))))
(or
@@ -1472,7 +1475,7 @@ regexp short cuts work. FP is the function defun information."
((looking-at "[\\!?;:.)]")
;; These are ok
nil)
- ((and checkdoc-permit-comma-termination-flag (looking-at ","))
+ ((and checkdoc-permit-comma-termination-flag (= (following-char) ?,))
nil)
(t
;; If it is not a complete sentence, let's see if we can
@@ -1638,6 +1641,17 @@ function,command,variable,option or symbol." ms1))))))
;; * If a user option variable records a true-or-false
;; condition, give it a name that ends in `-flag'.
+ ;; "True ..." should be "Non-nil ..."
+ (when (looking-at "\"\\*?\\(True\\)\\b")
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 1) (match-end 1)
+ "Say \"Non-nil\" instead of \"True\"? "
+ "Non-nil")
+ nil
+ (checkdoc-create-error
+ "\"True\" should usually be \"Non-nil\""
+ (match-beginning 1) (match-end 1))))
+
;; If the variable has -flag in the name, make sure
(if (and (string-match "-flag$" (car fp))
(not (looking-at "\"\\*?Non-nil\\s-+means\\s-+")))
@@ -1798,6 +1812,16 @@ Replace with \"%s\"? " original replace)
"Probably \"%s\" should be imperative \"%s\""
original replace)
(match-beginning 1) (match-end 1))))))
+ ;; "Return true ..." should be "Return non-nil ..."
+ (when (looking-at "\"Return \\(true\\)\\b")
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 1) (match-end 1)
+ "Say \"non-nil\" instead of \"true\"? "
+ "non-nil")
+ nil
+ (checkdoc-create-error
+ "\"true\" should usually be \"non-nil\""
+ (match-beginning 1) (match-end 1))))
;; Done with functions
)))
;;* When a documentation string refers to a Lisp symbol, write it as
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index 749061b7bc5..3852ceb6c31 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -89,7 +89,7 @@ strings case-insensitively."
;;; Control structures.
;;;###autoload
-(defun cl--mapcar-many (cl-func cl-seqs)
+(defun cl--mapcar-many (cl-func cl-seqs &optional acc)
(if (cdr (cdr cl-seqs))
(let* ((cl-res nil)
(cl-n (apply 'min (mapcar 'length cl-seqs)))
@@ -106,20 +106,23 @@ strings case-insensitively."
(setcar cl-p1 (cdr (car cl-p1))))
(aref (car cl-p1) cl-i)))
(setq cl-p1 (cdr cl-p1) cl-p2 (cdr cl-p2)))
- (push (apply cl-func cl-args) cl-res)
+ (if acc
+ (push (apply cl-func cl-args) cl-res)
+ (apply cl-func cl-args))
(setq cl-i (1+ cl-i)))
- (nreverse cl-res))
+ (and acc (nreverse cl-res)))
(let ((cl-res nil)
(cl-x (car cl-seqs))
(cl-y (nth 1 cl-seqs)))
(let ((cl-n (min (length cl-x) (length cl-y)))
(cl-i -1))
(while (< (setq cl-i (1+ cl-i)) cl-n)
- (push (funcall cl-func
- (if (consp cl-x) (pop cl-x) (aref cl-x cl-i))
- (if (consp cl-y) (pop cl-y) (aref cl-y cl-i)))
- cl-res)))
- (nreverse cl-res))))
+ (let ((val (funcall cl-func
+ (if (consp cl-x) (pop cl-x) (aref cl-x cl-i))
+ (if (consp cl-y) (pop cl-y) (aref cl-y cl-i)))))
+ (when acc
+ (push val cl-res)))))
+ (and acc (nreverse cl-res)))))
;;;###autoload
(defun cl-map (cl-type cl-func cl-seq &rest cl-rest)
@@ -142,7 +145,7 @@ the elements themselves.
(while (not (memq nil cl-args))
(push (apply cl-func cl-args) cl-res)
(setq cl-p cl-args)
- (while cl-p (setcar cl-p (cdr (pop cl-p)) )))
+ (while cl-p (setcar cl-p (cdr (pop cl-p)))))
(nreverse cl-res))
(let ((cl-res nil))
(while cl-list
@@ -155,8 +158,14 @@ the elements themselves.
"Like `cl-mapcar', but does not accumulate values returned by the function.
\n(fn FUNCTION SEQUENCE...)"
(if cl-rest
- (progn (apply 'cl-map nil cl-func cl-seq cl-rest)
- cl-seq)
+ (if (or (cdr cl-rest) (nlistp cl-seq) (nlistp (car cl-rest)))
+ (progn
+ (cl--mapcar-many cl-func (cons cl-seq cl-rest))
+ cl-seq)
+ (let ((cl-x cl-seq) (cl-y (car cl-rest)))
+ (while (and cl-x cl-y)
+ (funcall cl-func (pop cl-x) (pop cl-y)))
+ cl-seq))
(mapc cl-func cl-seq)))
;;;###autoload
@@ -164,7 +173,12 @@ the elements themselves.
"Like `cl-maplist', but does not accumulate values returned by the function.
\n(fn FUNCTION LIST...)"
(if cl-rest
- (apply 'cl-maplist cl-func cl-list cl-rest)
+ (let ((cl-args (cons cl-list (copy-sequence cl-rest)))
+ cl-p)
+ (while (not (memq nil cl-args))
+ (apply cl-func cl-args)
+ (setq cl-p cl-args)
+ (while cl-p (setcar cl-p (cdr (pop cl-p))))))
(let ((cl-p cl-list))
(while cl-p (funcall cl-func cl-p) (setq cl-p (cdr cl-p)))))
cl-list)
@@ -173,7 +187,9 @@ the elements themselves.
(defun cl-mapcan (cl-func cl-seq &rest cl-rest)
"Like `cl-mapcar', but nconc's together the values returned by the function.
\n(fn FUNCTION SEQUENCE...)"
- (apply 'nconc (apply 'cl-mapcar cl-func cl-seq cl-rest)))
+ (if cl-rest
+ (apply 'nconc (apply 'cl-mapcar cl-func cl-seq cl-rest))
+ (mapcan cl-func cl-seq)))
;;;###autoload
(defun cl-mapcon (cl-func cl-list &rest cl-rest)
@@ -591,13 +607,7 @@ too large if positive or too small if negative)."
\n(fn SYMBOL PROPNAME &optional DEFAULT)"
(declare (compiler-macro cl--compiler-macro-get)
(gv-setter (lambda (store) (ignore def) `(put ,sym ,tag ,store))))
- (or (get sym tag)
- (and def
- ;; Make sure `def' is really absent as opposed to set to nil.
- (let ((plist (symbol-plist sym)))
- (while (and plist (not (eq (car plist) tag)))
- (setq plist (cdr (cdr plist))))
- (if plist (car (cdr plist)) def)))))
+ (cl-getf (symbol-plist sym) tag def))
(autoload 'cl--compiler-macro-get "cl-macs")
;;;###autoload
@@ -616,26 +626,20 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
,(funcall setter
`(cl--set-getf ,getter ,k ,val))
,val)))))))))
- (setplist '--cl-getf-symbol-- plist)
- (or (get '--cl-getf-symbol-- tag)
- ;; Originally we called cl-get here,
- ;; but that fails, because cl-get has a compiler macro
- ;; definition that uses getf!
- (when def
- ;; Make sure `def' is really absent as opposed to set to nil.
- (while (and plist (not (eq (car plist) tag)))
- (setq plist (cdr (cdr plist))))
- (if plist (car (cdr plist)) def))))
+ (let ((val-tail (cdr-safe (plist-member plist tag))))
+ (if val-tail (car val-tail) def)))
;;;###autoload
(defun cl--set-getf (plist tag val)
- (let ((p plist))
- (while (and p (not (eq (car p) tag))) (setq p (cdr (cdr p))))
- (if p (progn (setcar (cdr p) val) plist) (cl-list* tag val plist))))
+ (let ((val-tail (cdr-safe (plist-member plist tag))))
+ (if val-tail (progn (setcar val-tail val) plist)
+ (cl-list* tag val plist))))
;;;###autoload
(defun cl--do-remf (plist tag)
(let ((p (cdr plist)))
+ ;; Can't use `plist-member' here because it goes to the cons-cell
+ ;; of TAG and we need the one before.
(while (and (cdr p) (not (eq (car (cdr p)) tag))) (setq p (cdr (cdr p))))
(and (cdr p) (progn (setcdr p (cdr (cdr (cdr p)))) t))))
@@ -771,8 +775,7 @@ including `cl-block' and `cl-eval-when'."
(defun cl--describe-class (type &optional class)
(unless class (setq class (cl--find-class type)))
(let ((location (find-lisp-object-file-name type 'define-type))
- ;; FIXME: Add a `cl-class-of' or `cl-typeof' or somesuch.
- (metatype (cl--class-name (symbol-value (aref class 0)))))
+ (metatype (type-of class)))
(insert (symbol-name type)
(substitute-command-keys " is a type (of kind `"))
(help-insert-xref-button (symbol-name metatype)
@@ -861,19 +864,65 @@ including `cl-block' and `cl-eval-when'."
"\n")))
"\n"))
+(defun cl--print-table (header rows)
+ ;; FIXME: Isn't this functionality already implemented elsewhere?
+ (let ((cols (apply #'vector (mapcar #'string-width header)))
+ (col-space 2))
+ (dolist (row rows)
+ (dotimes (i (length cols))
+ (let* ((x (pop row))
+ (curwidth (aref cols i))
+ (newwidth (if x (string-width x) 0)))
+ (if (> newwidth curwidth)
+ (setf (aref cols i) newwidth)))))
+ (let ((formats '())
+ (col 0))
+ (dotimes (i (length cols))
+ (push (concat (propertize " "
+ 'display
+ `(space :align-to ,(+ col col-space)))
+ "%s")
+ formats)
+ (cl-incf col (+ col-space (aref cols i))))
+ (let ((format (mapconcat #'identity (nreverse formats) "")))
+ (insert (apply #'format format
+ (mapcar (lambda (str) (propertize str 'face 'italic))
+ header))
+ "\n")
+ (insert (apply #'format format
+ (mapcar (lambda (str) (make-string (string-width str) ?—))
+ header))
+ "\n")
+ (dolist (row rows)
+ (insert (apply #'format format row) "\n"))))))
+
(defun cl--describe-class-slots (class)
"Print help description for the slots in CLASS.
Outputs to the current buffer."
(let* ((slots (cl--class-slots class))
- ;; FIXME: Add a `cl-class-of' or `cl-typeof' or somesuch.
- (metatype (cl--class-name (symbol-value (aref class 0))))
+ (metatype (type-of class))
;; ¡For EIEIO!
(cslots (condition-case nil
(cl-struct-slot-value metatype 'class-slots class)
(cl-struct-unknown-slot nil))))
(insert (propertize "Instance Allocated Slots:\n\n"
'face 'bold))
- (mapc #'cl--describe-class-slot slots)
+ (let* ((has-doc nil)
+ (slots-strings
+ (mapcar
+ (lambda (slot)
+ (list (cl-prin1-to-string (cl--slot-descriptor-name slot))
+ (cl-prin1-to-string (cl--slot-descriptor-type slot))
+ (cl-prin1-to-string (cl--slot-descriptor-initform slot))
+ (let ((doc (alist-get :documentation
+ (cl--slot-descriptor-props slot))))
+ (if (not doc) ""
+ (setq has-doc t)
+ (substitute-command-keys doc)))))
+ slots)))
+ (cl--print-table `("Name" "Type" "Default" . ,(if has-doc '("Doc")))
+ slots-strings))
+ (insert "\n")
(when (> (length cslots) 0)
(insert (propertize "\nClass Allocated Slots:\n\n" 'face 'bold))
(mapc #'cl--describe-class-slot cslots))))
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 8a59aa306b7..c64376b940f 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -226,7 +226,14 @@ DEFAULT-BODY, if present, is used as the body of a default method.
(when (eq 'setf (car-safe name))
(require 'gv)
(setq name (gv-setter (cadr name))))
- `(progn
+ `(prog1
+ (progn
+ (defalias ',name
+ (cl-generic-define ',name ',args ',(nreverse options))
+ ,(help-add-fundoc-usage doc args))
+ :autoload-end
+ ,@(mapcar (lambda (method) `(cl-defmethod ,name ,@method))
+ (nreverse methods)))
,@(mapcar (lambda (declaration)
(let ((f (cdr (assq (car declaration)
defun-declarations-alist))))
@@ -235,12 +242,7 @@ DEFAULT-BODY, if present, is used as the body of a default method.
(t (message "Warning: Unknown defun property `%S' in %S"
(car declaration) name)
nil))))
- (cdr declarations))
- (defalias ',name
- (cl-generic-define ',name ',args ',(nreverse options))
- ,(help-add-fundoc-usage doc args))
- ,@(mapcar (lambda (method) `(cl-defmethod ,name ,@method))
- (nreverse methods)))))
+ (cdr declarations)))))
;;;###autoload
(defun cl-generic-define (name args options)
@@ -358,6 +360,26 @@ the specializer used will be the one returned by BODY."
,nbody))))))
(f (error "Unexpected macroexpansion result: %S" f))))))
+(put 'cl-defmethod 'function-documentation
+ '(cl--generic-make-defmethod-docstring))
+
+(defun cl--generic-make-defmethod-docstring ()
+ ;; FIXME: Copy&paste from pcase--make-docstring.
+ (let* ((main (documentation (symbol-function 'cl-defmethod) 'raw))
+ (ud (help-split-fundoc main 'cl-defmethod)))
+ ;; So that eg emacs -Q -l cl-lib --eval "(documentation 'pcase)" works,
+ ;; where cl-lib is anything using pcase-defmacro.
+ (require 'help-fns)
+ (with-temp-buffer
+ (insert (or (cdr ud) main))
+ (insert "\n\n\tCurrently supported forms for TYPE:\n\n")
+ (dolist (method (reverse (cl--generic-method-table
+ (cl--generic 'cl-generic-generalizers))))
+ (let* ((info (cl--generic-method-info method)))
+ (when (nth 2 info)
+ (insert (nth 2 info) "\n\n"))))
+ (let ((combined-doc (buffer-string)))
+ (if ud (help-add-fundoc-usage combined-doc (car ud)) combined-doc)))))
;;;###autoload
(defmacro cl-defmethod (name args &rest body)
@@ -375,24 +397,28 @@ modifies how the method is combined with other methods, including:
:after - Method will be called after the primary
:around - Method will be called around everything else
The absence of QUALIFIER means this is a \"primary\" method.
+The set of acceptable qualifiers and their meaning is defined
+\(and can be extended) by the methods of `cl-generic-combine-methods'.
-TYPE can be one of the basic types (see the full list and their
-hierarchy in `cl--generic-typeof-types'), CL struct type, or an
-EIEIO class.
+ARGS can also include so-called context specializers, introduced by
+`&context' (which should appear right after the mandatory arguments,
+before any &optional or &rest). They have the form (EXPR TYPE) where
+EXPR is an Elisp expression whose value should match TYPE for the
+method to be applicable.
-Other than that, TYPE can also be of the form `(eql VAL)' in
-which case this method will be invoked when the argument is `eql'
-to VAL, or `(head VAL)', in which case the argument is required
-to be a cons with VAL as its head.
+The set of acceptable TYPEs (also called \"specializers\") is defined
+\(and can be extended) by the various methods of `cl-generic-generalizers'.
\(fn NAME [QUALIFIER] ARGS &rest [DOCSTRING] BODY)"
(declare (doc-string 3) (indent 2)
(debug
(&define ; this means we are defining something
- [&or name ("setf" :name setf name)]
+ [&or name ("setf" name :name setf)]
;; ^^ This is the methods symbol
- [ &optional keywordp ] ; this is key :before etc
- list ; arguments
+ [ &rest atom ] ; Multiple qualifiers are allowed.
+ ; Like in CLOS spec, we support
+ ; any non-list values.
+ cl-generic-method-args ; arguments
[ &optional stringp ] ; documentation string
def-body))) ; part to be debugged
(let ((qualifiers nil))
@@ -415,7 +441,8 @@ to be a cons with VAL as its head.
;; function, so warnings like "not known to be defined" are fair game.
;; But in practice, it's common to use `cl-defmethod'
;; without a previous `cl-defgeneric'.
- (declare-function ,name "")
+ ;; The ",'" is a no-op that pacifies check-declare.
+ (,'declare-function ,name "")
(cl-generic-define-method ',name ',(nreverse qualifiers) ',args
,uses-cnm ,fun)))))
@@ -428,6 +455,12 @@ to be a cons with VAL as its head.
(setq methods (cdr methods)))
methods)
+(defun cl--generic-load-hist-format (name qualifiers specializers)
+ ;; FIXME: This function is used in elisp-mode.el and
+ ;; elisp-mode-tests.el, but I still decided to use an internal name
+ ;; because these uses should be removed or moved into cl-generic.el.
+ `(,name ,qualifiers . ,specializers))
+
;;;###autoload
(defun cl-generic-define-method (name qualifiers args uses-cnm function)
(pcase-let*
@@ -468,7 +501,9 @@ to be a cons with VAL as its head.
(cons method mt)
;; Keep the ordering; important for methods with :extra qualifiers.
(mapcar (lambda (x) (if (eq x (car me)) method x)) mt)))
- (cl-pushnew `(cl-defmethod . (,(cl--generic-name generic) . ,specializers))
+ (cl-pushnew `(cl-defmethod . ,(cl--generic-load-hist-format
+ (cl--generic-name generic)
+ qualifiers specializers))
current-load-list :test #'equal)
;; FIXME: Try to avoid re-constructing a new function if the old one
;; is still valid (e.g. still empty method cache)?
@@ -750,7 +785,7 @@ methods.")
(fset 'cl-generic-combine-methods #'cl--generic-standard-method-combination))
(cl-defmethod cl-generic-generalizers (specializer)
- "Support for the catch-all t specializer."
+ "Support for the catch-all t specializer which always matches."
(if (eq specializer t) (list cl--generic-t-generalizer)
(error "Unknown specializer %S" specializer)))
@@ -854,18 +889,22 @@ Can only be used from within the lexical body of a primary or around method."
(defun cl--generic-search-method (met-name)
"For `find-function-regexp-alist'. Searches for a cl-defmethod.
-MET-NAME is a cons (SYMBOL . SPECIALIZERS)."
+MET-NAME is as returned by `cl--generic-load-hist-format'."
(let ((base-re (concat "(\\(?:cl-\\)?defmethod[ \t]+"
(regexp-quote (format "%s" (car met-name)))
"\\_>")))
(or
(re-search-forward
(concat base-re "[^&\"\n]*"
+ (mapconcat (lambda (qualifier)
+ (regexp-quote (format "%S" qualifier)))
+ (cadr met-name)
+ "[ \t\n]*")
(mapconcat (lambda (specializer)
(regexp-quote
(format "%S" (if (consp specializer)
(nth 1 specializer) specializer))))
- (remq t (cdr met-name))
+ (remq t (cddr met-name))
"[ \t\n]*)[^&\"\n]*"))
nil t)
(re-search-forward base-re nil t))))
@@ -922,8 +961,10 @@ MET-NAME is a cons (SYMBOL . SPECIALIZERS)."
(let* ((info (cl--generic-method-info method)))
;; FIXME: Add hyperlinks for the types as well.
(insert (format "%s%S" (nth 0 info) (nth 1 info)))
- (let* ((met-name (cons function
- (cl--generic-method-specializers method)))
+ (let* ((met-name (cl--generic-load-hist-format
+ function
+ (cl--generic-method-qualifiers method)
+ (cl--generic-method-specializers method)))
(file (find-lisp-object-file-name met-name 'cl-defmethod)))
(when file
(insert (substitute-command-keys " in `"))
@@ -1007,7 +1048,8 @@ The value returned is a list of elements of the form
(lambda (tag &rest _) (if (eq (car-safe tag) 'head) (list tag))))
(cl-defmethod cl-generic-generalizers :extra "head" (specializer)
- "Support for the `(head VAL)' specializers."
+ "Support for (head VAL) specializers.
+These match if the argument is a cons cell whose car is `eql' to VAL."
;; We have to implement `head' here using the :extra qualifier,
;; since we can't use the `head' specializer to implement itself.
(if (not (eq (car-safe specializer) 'head))
@@ -1027,7 +1069,8 @@ The value returned is a list of elements of the form
(lambda (tag &rest _) (if (eq (car-safe tag) 'eql) (list tag))))
(cl-defmethod cl-generic-generalizers ((specializer (head eql)))
- "Support for the `(eql VAL)' specializers."
+ "Support for (eql VAL) specializers.
+These match if the argument is `eql' to VAL."
(puthash (cadr specializer) specializer cl--generic-eql-used)
(list cl--generic-eql-generalizer))
@@ -1041,24 +1084,8 @@ The value returned is a list of elements of the form
;;; Support for cl-defstructs specializers.
(defun cl--generic-struct-tag (name &rest _)
- ;; It's tempting to use (and (vectorp ,name) (aref ,name 0))
- ;; but that would suffer from some problems:
- ;; - the vector may have size 0.
- ;; - when called on an actual vector (rather than an object), we'd
- ;; end up returning an arbitrary value, possibly colliding with
- ;; other tagcode's values.
- ;; - it can also result in returning all kinds of irrelevant
- ;; values which would end up filling up the method-cache with
- ;; lots of irrelevant/redundant entries.
- ;; FIXME: We could speed this up by introducing a dedicated
- ;; vector type at the C level, so we could do something like
- ;; (and (vector-objectp ,name) (aref ,name 0))
- `(and (vectorp ,name)
- (> (length ,name) 0)
- (let ((tag (aref ,name 0)))
- (and (symbolp tag)
- (eq (symbol-function tag) :quick-object-witness-check)
- tag))))
+ ;; Use exactly the same code as for `typeof'.
+ `(if ,name (type-of ,name) 'null))
(defun cl--generic-class-parents (class)
(let ((parents ())
@@ -1072,8 +1099,8 @@ The value returned is a list of elements of the form
(nreverse parents)))
(defun cl--generic-struct-specializers (tag &rest _)
- (and (symbolp tag) (boundp tag)
- (let ((class (symbol-value tag)))
+ (and (symbolp tag)
+ (let ((class (get tag 'cl--class)))
(when (cl-typep class 'cl-structure-class)
(cl--generic-class-parents class)))))
@@ -1082,7 +1109,7 @@ The value returned is a list of elements of the form
#'cl--generic-struct-specializers)
(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type)
- "Support for dispatch on cl-struct types."
+ "Support for dispatch on types defined by `cl-defstruct'."
(or
(when (symbolp type)
;; Use the "cl--struct-class*" (inlinable) functions/macros rather than
@@ -1103,21 +1130,29 @@ The value returned is a list of elements of the form
(defconst cl--generic-typeof-types
;; Hand made from the source code of `type-of'.
- '((integer number) (symbol) (string array sequence) (cons list sequence)
+ '((integer number number-or-marker atom)
+ (symbol atom) (string array sequence atom)
+ (cons list sequence)
;; Markers aren't `numberp', yet they are accepted wherever integers are
;; accepted, pretty much.
- (marker) (overlay) (float number) (window-configuration)
- (process) (window) (subr) (compiled-function) (buffer)
- (char-table array sequence)
- (bool-vector array sequence)
- (frame) (hash-table) (font-spec) (font-entity) (font-object)
- (vector array sequence)
- ;; Plus, hand made:
- (null symbol list sequence)
- (list sequence)
- (array sequence)
- (sequence)
- (number)))
+ (marker number-or-marker atom)
+ (overlay atom) (float number atom) (window-configuration atom)
+ (process atom) (window atom) (subr atom) (compiled-function function atom)
+ (buffer atom) (char-table array sequence atom)
+ (bool-vector array sequence atom)
+ (frame atom) (hash-table atom) (terminal atom)
+ (thread atom) (mutex atom) (condvar atom)
+ (font-spec atom) (font-entity atom) (font-object atom)
+ (vector array sequence atom)
+ ;; Plus, really hand made:
+ (null symbol list sequence atom))
+ "Alist of supertypes.
+Each element has the form (TYPE . SUPERTYPES) where TYPE is one of
+the symbols returned by `type-of', and SUPERTYPES is the list of its
+supertypes from the most specific to least specific.")
+
+(defconst cl--generic-all-builtin-types
+ (delete-dups (copy-sequence (apply #'append cl--generic-typeof-types))))
(cl-generic-define-generalizer cl--generic-typeof-generalizer
;; FIXME: We could also change `type-of' to return `null' for nil.
@@ -1126,11 +1161,12 @@ The value returned is a list of elements of the form
(and (symbolp tag) (assq tag cl--generic-typeof-types))))
(cl-defmethod cl-generic-generalizers :extra "typeof" (type)
- "Support for dispatch on builtin types."
+ "Support for dispatch on builtin types.
+See the full list and their hierarchy in `cl--generic-typeof-types'."
;; FIXME: Add support for other types accepted by `cl-typep' such
- ;; as `character', `atom', `face', `function', ...
+ ;; as `character', `face', `function', ...
(or
- (and (assq type cl--generic-typeof-types)
+ (and (memq type cl--generic-all-builtin-types)
(progn
;; FIXME: While this wrinkle in the semantics can be occasionally
;; problematic, this warning is more often annoying than helpful.
@@ -1164,7 +1200,8 @@ The value returned is a list of elements of the form
#'cl--generic-derived-specializers)
(cl-defmethod cl-generic-generalizers ((_specializer (head derived-mode)))
- "Support for the `(derived-mode MODE)' specializers."
+ "Support for (derived-mode MODE) specializers.
+Used internally for the (major-mode MODE) context specializers."
(list cl--generic-derived-generalizer))
(cl-generic-define-context-rewriter major-mode (mode &rest modes)
@@ -1173,9 +1210,5 @@ The value returned is a list of elements of the form
(progn (cl-assert (null modes)) mode)
`(derived-mode ,mode . ,modes))))
-;; Local variables:
-;; generated-autoload-file: "cl-loaddefs.el"
-;; End:
-
(provide 'cl-generic)
;;; cl-generic.el ends here
diff --git a/lisp/emacs-lisp/cl-indent.el b/lisp/emacs-lisp/cl-indent.el
index 33ecf3f4542..df0e0a88583 100644
--- a/lisp/emacs-lisp/cl-indent.el
+++ b/lisp/emacs-lisp/cl-indent.el
@@ -1,4 +1,4 @@
-;;; cl-indent.el --- enhanced lisp-indent mode
+;;; cl-indent.el --- Enhanced lisp-indent mode -*- lexical-binding:t -*-
;; Copyright (C) 1987, 2000-2017 Free Software Foundation, Inc.
@@ -35,7 +35,7 @@
;;; Code:
-(eval-when-compile (require 'cl))
+(eval-when-compile (require 'cl-lib))
(defgroup lisp-indent nil
"Indentation in Lisp."
@@ -166,7 +166,7 @@ is set to `defun'.")
(forward-char 1)
(forward-sexp 2)
(backward-sexp 1)
- (looking-at "\\sw"))
+ (looking-at "\\(:\\|\\sw\\)"))
(error t)))
(defun lisp-indent-find-method (symbol &optional no-compat)
@@ -187,13 +187,13 @@ the standard lisp indent package."
(when (and (eq lisp-indent-backquote-substitution-mode 'corrected))
(save-excursion
(goto-char (elt state 1))
- (incf loop-indentation
- (cond ((eq (char-before) ?,) -1)
- ((and (eq (char-before) ?@)
- (progn (backward-char)
- (eq (char-before) ?,)))
- -2)
- (t 0)))))
+ (cl-incf loop-indentation
+ (cond ((eq (char-before) ?,) -1)
+ ((and (eq (char-before) ?@)
+ (progn (backward-char)
+ (eq (char-before) ?,)))
+ -2)
+ (t 0)))))
(goto-char indent-point)
(beginning-of-line)
@@ -315,7 +315,6 @@ instead."
;; If non-nil, this is an indentation to use
;; if nothing else specifies it more firmly.
tentative-calculated
- (last-point indent-point)
;; the position of the open-paren of the innermost containing list
(containing-form-start (elt state 1))
;; the column of the above
@@ -410,9 +409,9 @@ instead."
;; ",(...)" or ",@(...)"
(when (eq lisp-indent-backquote-substitution-mode
'corrected)
- (incf sexp-column -1)
+ (cl-incf sexp-column -1)
(when (eq (char-after (1- containing-sexp)) ?\@)
- (incf sexp-column -1)))
+ (cl-incf sexp-column -1)))
(cond (lisp-indent-backquote-substitution-mode
(setf tentative-calculated normal-indent)
(setq depth lisp-indent-maximum-backtracking)
@@ -465,7 +464,6 @@ instead."
function method path state indent-point
sexp-column normal-indent)))))
(goto-char containing-sexp)
- (setq last-point containing-sexp)
(unless calculated
(condition-case ()
(progn (backward-up-list 1)
@@ -474,6 +472,9 @@ instead."
(or calculated tentative-calculated))))
+;; Dynamically bound in common-lisp-indent-call-method.
+(defvar lisp-indent-error-function)
+
(defun common-lisp-indent-call-method (function method path state indent-point
sexp-column normal-indent)
(let ((lisp-indent-error-function function))
@@ -484,9 +485,6 @@ instead."
(lisp-indent-259 method path state indent-point
sexp-column normal-indent))))
-;; Dynamically bound in common-lisp-indent-call-method.
-(defvar lisp-indent-error-function)
-
(defun lisp-indent-report-bad-format (m)
(error "%s has a badly-formed %s property: %s"
;; Love those free variable references!!
@@ -717,7 +715,7 @@ optional\\|rest\\|key\\|allow-other-keys\\|aux\\|whole\\|body\\|environment\
(forward-sexp 2)
(skip-chars-forward " \t\n")
(while (looking-at "\\sw\\|\\s_")
- (incf nqual)
+ (cl-incf nqual)
(forward-sexp)
(skip-chars-forward " \t\n"))
(> nqual 0)))
@@ -726,7 +724,7 @@ optional\\|rest\\|key\\|allow-other-keys\\|aux\\|whole\\|body\\|environment\
path state indent-point sexp-column normal-indent))
-(defun lisp-indent-function-lambda-hack (path state indent-point
+(defun lisp-indent-function-lambda-hack (path _state _indent-point
sexp-column normal-indent)
;; indent (function (lambda () <newline> <body-forms>)) kludgily.
(if (or (cdr path) ; wtf?
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index b1db07fe165..936c852526c 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -347,7 +347,7 @@ Call `cl-float-limits' to set this.")
(cl--defalias 'cl-copy-seq 'copy-sequence)
-(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs))
+(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs &optional acc))
(defun cl-mapcar (cl-func cl-x &rest cl-rest)
"Apply FUNCTION to each element of SEQ, and make a list of the results.
@@ -358,7 +358,7 @@ SEQ, this is like `mapcar'. With several, it is like the Common Lisp
\n(fn FUNCTION SEQ...)"
(if cl-rest
(if (or (cdr cl-rest) (nlistp cl-x) (nlistp (car cl-rest)))
- (cl--mapcar-many cl-func (cons cl-x cl-rest))
+ (cl--mapcar-many cl-func (cons cl-x cl-rest) 'accumulate)
(let ((cl-res nil) (cl-y (car cl-rest)))
(while (and cl-x cl-y)
(push (funcall cl-func (pop cl-x) (pop cl-y)) cl-res))
@@ -413,125 +413,30 @@ Signal an error if X is not a list."
(declare (gv-setter (lambda (store) `(setcar (nthcdr 9 ,x) ,store))))
(nth 9 x))
-(defun cl-caaar (x)
- "Return the `car' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (car x))))
-
-(defun cl-caadr (x)
- "Return the `car' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (cdr x))))
-
-(defun cl-cadar (x)
- "Return the `car' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (car x))))
-
-(defun cl-caddr (x)
- "Return the `car' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (cdr x))))
-
-(defun cl-cdaar (x)
- "Return the `cdr' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (car x))))
-
-(defun cl-cdadr (x)
- "Return the `cdr' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (cdr x))))
-
-(defun cl-cddar (x)
- "Return the `cdr' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (car x))))
-
-(defun cl-cdddr (x)
- "Return the `cdr' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (cdr x))))
-
-(defun cl-caaaar (x)
- "Return the `car' of the `car' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (car (car x)))))
-
-(defun cl-caaadr (x)
- "Return the `car' of the `car' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (car (cdr x)))))
-
-(defun cl-caadar (x)
- "Return the `car' of the `car' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (cdr (car x)))))
-
-(defun cl-caaddr (x)
- "Return the `car' of the `car' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (car (cdr (cdr x)))))
-
-(defun cl-cadaar (x)
- "Return the `car' of the `cdr' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (car (car x)))))
-
-(defun cl-cadadr (x)
- "Return the `car' of the `cdr' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (car (cdr x)))))
-
-(defun cl-caddar (x)
- "Return the `car' of the `cdr' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (cdr (car x)))))
-
-(defun cl-cadddr (x)
- "Return the `car' of the `cdr' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (car (cdr (cdr (cdr x)))))
-
-(defun cl-cdaaar (x)
- "Return the `cdr' of the `car' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (car (car x)))))
-
-(defun cl-cdaadr (x)
- "Return the `cdr' of the `car' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (car (cdr x)))))
-
-(defun cl-cdadar (x)
- "Return the `cdr' of the `car' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (cdr (car x)))))
-
-(defun cl-cdaddr (x)
- "Return the `cdr' of the `car' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (car (cdr (cdr x)))))
-
-(defun cl-cddaar (x)
- "Return the `cdr' of the `cdr' of the `car' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (car (car x)))))
-
-(defun cl-cddadr (x)
- "Return the `cdr' of the `cdr' of the `car' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (car (cdr x)))))
-
-(defun cl-cdddar (x)
- "Return the `cdr' of the `cdr' of the `cdr' of the `car' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (cdr (car x)))))
-
-(defun cl-cddddr (x)
- "Return the `cdr' of the `cdr' of the `cdr' of the `cdr' of X."
- (declare (compiler-macro internal--compiler-macro-cXXr))
- (cdr (cdr (cdr (cdr x)))))
+(defalias 'cl-caaar 'caaar)
+(defalias 'cl-caadr 'caadr)
+(defalias 'cl-cadar 'cadar)
+(defalias 'cl-caddr 'caddr)
+(defalias 'cl-cdaar 'cdaar)
+(defalias 'cl-cdadr 'cdadr)
+(defalias 'cl-cddar 'cddar)
+(defalias 'cl-cdddr 'cdddr)
+(defalias 'cl-caaaar 'caaaar)
+(defalias 'cl-caaadr 'caaadr)
+(defalias 'cl-caadar 'caadar)
+(defalias 'cl-caaddr 'caaddr)
+(defalias 'cl-cadaar 'cadaar)
+(defalias 'cl-cadadr 'cadadr)
+(defalias 'cl-caddar 'caddar)
+(defalias 'cl-cadddr 'cadddr)
+(defalias 'cl-cdaaar 'cdaaar)
+(defalias 'cl-cdaadr 'cdaadr)
+(defalias 'cl-cdadar 'cdadar)
+(defalias 'cl-cdaddr 'cdaddr)
+(defalias 'cl-cddaar 'cddaar)
+(defalias 'cl-cddadr 'cddadr)
+(defalias 'cl-cdddar 'cdddar)
+(defalias 'cl-cddddr 'cddddr)
;;(defun last* (x &optional n)
;; "Returns the last link in the list LIST.
@@ -733,6 +638,42 @@ If ALIST is non-nil, the new pairs are prepended to it."
(require 'cl-macs)
(require 'cl-seq))
+(defun cl--old-struct-type-of (orig-fun object)
+ (or (and (vectorp object)
+ (let ((tag (aref object 0)))
+ (when (and (symbolp tag)
+ (string-prefix-p "cl-struct-" (symbol-name tag)))
+ (unless (eq (symbol-function tag)
+ :quick-object-witness-check)
+ ;; Old-style old-style struct:
+ ;; Convert to new-style old-style struct!
+ (let* ((type (intern (substring (symbol-name tag)
+ (length "cl-struct-"))))
+ (class (cl--struct-get-class type)))
+ ;; If the `cl-defstruct' was recompiled after the code
+ ;; which constructed `object', `cl--struct-get-class' may
+ ;; not have called `cl-struct-define' and setup the tag
+ ;; symbol for us.
+ (unless (eq (symbol-function tag)
+ :quick-object-witness-check)
+ (set tag class)
+ (fset tag :quick-object-witness-check))))
+ (cl--class-name (symbol-value tag)))))
+ (funcall orig-fun object)))
+
+;;;###autoload
+(define-minor-mode cl-old-struct-compat-mode
+ "Enable backward compatibility with old-style structs.
+This can be needed when using code byte-compiled using the old
+macro-expansion of `cl-defstruct' that used vectors objects instead
+of record objects."
+ :global t
+ (cond
+ (cl-old-struct-compat-mode
+ (advice-add 'type-of :around #'cl--old-struct-type-of))
+ (t
+ (advice-remove 'type-of #'cl--old-struct-type-of))))
+
;; Local variables:
;; byte-compile-dynamic: t
;; End:
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 928f5d87f8f..b1ada00f4a4 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -923,6 +923,7 @@ For more details, see Info node `(cl)Loop Facility'.
"count" "maximize" "minimize" "if" "unless"
"return"]
form]
+ ["using" (symbolp symbolp)]
;; Simple default, which covers 99% of the cases.
symbolp form)))
(if (not (memq t (mapcar #'symbolp
@@ -1837,6 +1838,27 @@ Labels have lexical scope and dynamic extent."
`(throw ',catch-tag ',label))))
,@macroexpand-all-environment)))))
+(defun cl--prog (binder bindings body)
+ (let (decls)
+ (while (eq 'declare (car-safe (car body)))
+ (push (pop body) decls))
+ `(cl-block nil
+ (,binder ,bindings
+ ,@(nreverse decls)
+ (cl-tagbody . ,body)))))
+
+;;;###autoload
+(defmacro cl-prog (bindings &rest body)
+ "Run BODY like a `cl-tagbody' after setting up the BINDINGS.
+Shorthand for (cl-block nil (let BINDINGS (cl-tagbody BODY)))"
+ (cl--prog 'let bindings body))
+
+;;;###autoload
+(defmacro cl-prog* (bindings &rest body)
+ "Run BODY like a `cl-tagbody' after setting up the BINDINGS.
+Shorthand for (cl-block nil (let* BINDINGS (cl-tagbody BODY)))"
+ (cl--prog 'let* bindings body))
+
;;;###autoload
(defmacro cl-do-symbols (spec &rest body)
"Loop over all symbols.
@@ -2030,15 +2052,17 @@ This is like `cl-flet', but for macros instead of functions.
This function replaces `macroexpand' during macro expansion
of `cl-symbol-macrolet', and does the same thing as `macroexpand'
except that it additionally expands symbol macros."
- (let ((macroexpand-all-environment env))
+ (let ((macroexpand-all-environment env)
+ (venv (alist-get :cl-symbol-macros env)))
(while
(progn
(setq exp (funcall cl--old-macroexpand exp env))
(pcase exp
((pred symbolp)
;; Perform symbol-macro expansion.
- (when (cdr (assq (symbol-name exp) env))
- (setq exp (cadr (assq (symbol-name exp) env)))))
+ (let ((symval (assq exp venv)))
+ (when symval
+ (setq exp (cadr symval)))))
(`(setq . ,_)
;; Convert setq to setf if required by symbol-macro expansion.
(let* ((args (mapcar (lambda (f) (cl--sm-macroexpand f env))
@@ -2056,7 +2080,7 @@ except that it additionally expands symbol macros."
(let ((letf nil) (found nil) (nbs ()))
(dolist (binding bindings)
(let* ((var (if (symbolp binding) binding (car binding)))
- (sm (assq (symbol-name var) env)))
+ (sm (assq var venv)))
(push (if (not (cdr sm))
binding
(let ((nexp (cadr sm)))
@@ -2113,30 +2137,29 @@ Within the body FORMs, references to the variable NAME will be replaced
by EXPANSION, and (setq NAME ...) will act like (setf EXPANSION ...).
\(fn ((NAME EXPANSION) ...) FORM...)"
- (declare (indent 1) (debug ((&rest (symbol sexp)) cl-declarations body)))
- (cond
- ((cdr bindings)
- `(cl-symbol-macrolet (,(car bindings))
- (cl-symbol-macrolet ,(cdr bindings) ,@body)))
- ((null bindings) (macroexp-progn body))
- (t
- (let ((previous-macroexpand (symbol-function 'macroexpand)))
- (unwind-protect
- (progn
- (fset 'macroexpand #'cl--sm-macroexpand)
- (let ((expansion
- ;; FIXME: For N bindings, this will traverse `body' N times!
- (macroexpand-all (macroexp-progn body)
- (cons (list (symbol-name (caar bindings))
- (cl-cadar bindings))
- macroexpand-all-environment))))
- (if (or (null (cdar bindings)) (cl-cddar bindings))
- (macroexp--warn-and-return
- (format-message "Malformed `cl-symbol-macrolet' binding: %S"
- (car bindings))
- expansion)
- expansion)))
- (fset 'macroexpand previous-macroexpand))))))
+ (declare (indent 1) (debug ((&rest (symbolp sexp)) cl-declarations body)))
+ (let ((previous-macroexpand (symbol-function 'macroexpand))
+ (malformed-bindings nil))
+ (dolist (binding bindings)
+ (unless (and (consp binding) (symbolp (car binding))
+ (consp (cdr binding)) (null (cddr binding)))
+ (push binding malformed-bindings)))
+ (unwind-protect
+ (progn
+ (fset 'macroexpand #'cl--sm-macroexpand)
+ (let* ((venv (cdr (assq :cl-symbol-macros macroexpand-all-environment)))
+ (expansion
+ (macroexpand-all (macroexp-progn body)
+ (cons (cons :cl-symbol-macros
+ (append bindings venv))
+ macroexpand-all-environment))))
+ (if malformed-bindings
+ (macroexp--warn-and-return
+ (format-message "Malformed `cl-symbol-macrolet' binding(s): %S"
+ (nreverse malformed-bindings))
+ expansion)
+ expansion)))
+ (fset 'macroexpand previous-macroexpand))))
;;; Multiple values.
@@ -2557,20 +2580,19 @@ non-nil value, that slot cannot be set via `setf'.
[&or symbolp
(gate
symbolp &rest
- (&or [":conc-name" symbolp]
- [":constructor" symbolp &optional cl-lambda-list]
- [":copier" symbolp]
- [":predicate" symbolp]
- [":include" symbolp &rest sexp] ;; Not finished.
- ;; The following are not supported.
- ;; [":print-function" ...]
- ;; [":type" ...]
- ;; [":initial-offset" ...]
- ))]
+ [&or symbolp
+ (&or [":conc-name" symbolp]
+ [":constructor" symbolp &optional cl-lambda-list]
+ [":copier" symbolp]
+ [":predicate" symbolp]
+ [":include" symbolp &rest sexp] ;; Not finished.
+ [":print-function" sexp]
+ [":type" symbolp]
+ [":named"]
+ [":initial-offset" natnump])])]
[&optional stringp]
;; All the above is for the following def-form.
- &rest &or symbolp (symbolp def-form
- &optional ":read-only" sexp))))
+ &rest &or symbolp (symbolp &optional def-form &rest sexp))))
(let* ((name (if (consp struct) (car struct) struct))
(opts (cdr-safe struct))
(slots nil)
@@ -2583,11 +2605,24 @@ non-nil value, that slot cannot be set via `setf'.
(print-func nil) (print-auto nil)
(safety (if (cl--compiling-file) cl--optimize-safety 3))
(include nil)
- (tag (intern (format "cl-struct-%s" name)))
+ ;; There are 4 types of structs:
+ ;; - `vector' type: means we should use a vector, which can come
+ ;; with or without a tag `name', which is usually in slot 0
+ ;; but obeys :initial-offset.
+ ;; - `list' type: same as `vector' but using lists.
+ ;; - `record' type: means we should use a record, which necessarily
+ ;; comes tagged in slot 0. Currently we'll use the `name' as
+ ;; the tag, but we may want to change it so that the class object
+ ;; is used as the tag.
+ ;; - nil type: this is the "pre-record default", which uses a vector
+ ;; with a tag in slot 0 which is a symbol of the form
+ ;; `cl-struct-NAME'. We need to still support this for backward
+ ;; compatibility with old .elc files.
+ (tag name)
(tag-symbol (intern (format "cl-struct-%s-tags" name)))
(include-descs nil)
(include-name nil)
- (type nil)
+ (type nil) ;nil here means not specified explicitly.
(named nil)
(forms nil)
(docstring (if (stringp (car descs)) (pop descs)))
@@ -2627,14 +2662,16 @@ non-nil value, that slot cannot be set via `setf'.
((eq opt :print-function)
(setq print-func (car args)))
((eq opt :type)
- (setq type (car args)))
+ (setq type (car args))
+ (unless (memq type '(vector list))
+ (error "Invalid :type specifier: %s" type)))
((eq opt :named)
(setq named t))
((eq opt :initial-offset)
(setq descs (nconc (make-list (car args) '(cl-skip-slot))
descs)))
(t
- (error "Slot option %s unrecognized" opt)))))
+ (error "Structure option %s unrecognized" opt)))))
(unless (or include-name type)
(setq include-name cl--struct-default-parent))
(when include-name (setq include (cl--struct-get-class include-name)))
@@ -2659,13 +2696,11 @@ non-nil value, that slot cannot be set via `setf'.
(pop include-descs)))
(setq descs (append old-descs (delq (assq 'cl-tag-slot descs) descs))
type inc-type
- named (if type (assq 'cl-tag-slot descs) 'true))
- (if (cl--struct-class-named include) (setq tag name named t)))
- (if type
- (progn
- (or (memq type '(vector list))
- (error "Invalid :type specifier: %s" type))
- (if named (setq tag name)))
+ named (if (memq type '(vector list))
+ (assq 'cl-tag-slot descs)
+ 'true))
+ (if (cl--struct-class-named include) (setq named t)))
+ (unless type
(setq named 'true)))
(or named (setq descs (delq (assq 'cl-tag-slot descs) descs)))
(when (and (null predicate) named)
@@ -2675,7 +2710,9 @@ non-nil value, that slot cannot be set via `setf'.
(length (memq (assq 'cl-tag-slot descs)
descs)))))
(cond
- ((memq type '(nil vector))
+ ((null type) ;Record type.
+ `(memq (type-of cl-x) ,tag-symbol))
+ ((eq type 'vector)
`(and (vectorp cl-x)
(>= (length cl-x) ,(length descs))
(memq (aref cl-x ,pos) ,tag-symbol)))
@@ -2698,7 +2735,7 @@ non-nil value, that slot cannot be set via `setf'.
(let ((pos 0) (descp descs))
(while descp
(let* ((desc (pop descp))
- (slot (car desc)))
+ (slot (pop desc)))
(if (memq slot '(cl-tag-slot cl-skip-slot))
(progn
(push nil slots)
@@ -2708,8 +2745,12 @@ non-nil value, that slot cannot be set via `setf'.
(error "Duplicate slots named %s in %s" slot name))
(let ((accessor (intern (format "%s%s" conc-name slot))))
(push slot slots)
- (push (nth 1 desc) defaults)
+ (push (pop desc) defaults)
+ ;; The arg "cl-x" is referenced by name in eg pred-form
+ ;; and pred-check, so changing it is not straightforward.
(push `(cl-defsubst ,accessor (cl-x)
+ ,(format "Access slot \"%s\" of `%s' struct CL-X."
+ slot struct)
(declare (side-effect-free t))
,@(and pred-check
(list `(or ,pred-check
@@ -2719,7 +2760,25 @@ non-nil value, that slot cannot be set via `setf'.
(if (= pos 0) '(car cl-x)
`(nth ,pos cl-x))))
forms)
- (if (cadr (memq :read-only (cddr desc)))
+ (when (cl-oddp (length desc))
+ (push
+ (macroexp--warn-and-return
+ (format "Missing value for option `%S' of slot `%s' in struct %s!"
+ (car (last desc)) slot name)
+ 'nil)
+ forms)
+ (when (and (keywordp (car defaults))
+ (not (keywordp (car desc))))
+ (let ((kw (car defaults)))
+ (push
+ (macroexp--warn-and-return
+ (format " I'll take `%s' to be an option rather than a default value."
+ kw)
+ 'nil)
+ forms)
+ (push kw desc)
+ (setcar defaults nil))))
+ (if (plist-get desc ':read-only)
(push `(gv-define-expander ,accessor
(lambda (_cl-do _cl-x)
(error "%s is a read-only slot" ',accessor)))
@@ -2750,7 +2809,8 @@ non-nil value, that slot cannot be set via `setf'.
(setq slots (nreverse slots)
defaults (nreverse defaults))
(and copier
- (push `(defalias ',copier #'copy-sequence) forms))
+ (push `(defalias ',copier #'copy-sequence)
+ forms))
(if constructor
(push (list constructor
(cons '&key (delq nil (copy-sequence slots))))
@@ -2765,7 +2825,7 @@ non-nil value, that slot cannot be set via `setf'.
(format "Constructor for objects of type `%s'." name))
,@(if (cl--safe-expr-p `(progn ,@(mapcar #'cl-second descs)))
'((declare (side-effect-free t))))
- (,(or type #'vector) ,@make))
+ (,(or type #'record) ,@make))
forms)))
(if print-auto (nconc print-func (list '(princ ")" cl-s) t)))
;; Don't bother adding to cl-custom-print-functions since it's not used
@@ -2787,8 +2847,8 @@ non-nil value, that slot cannot be set via `setf'.
;; struct as a parent.
(eval-and-compile
(cl-struct-define ',name ,docstring ',include-name
- ',type ,(eq named t) ',descs ',tag-symbol ',tag
- ',print-auto))
+ ',(or type 'record) ,(eq named t) ',descs
+ ',tag-symbol ',tag ',print-auto))
',name)))
;;; Add cl-struct support to pcase
@@ -2823,6 +2883,15 @@ is a shorthand for (NAME NAME)."
,pat)))
fields)))
+(defun cl--defstruct-predicate (type)
+ (let ((cons (assq (cl-struct-sequence-type type)
+ `((list . consp)
+ (vector . vectorp)
+ (nil . recordp)))))
+ (if cons
+ (cdr cons)
+ 'recordp)))
+
(defun cl--pcase-mutually-exclusive-p (orig pred1 pred2)
"Extra special cases for `cl-typep' predicates."
(let* ((x1 pred1) (x2 pred2)
@@ -2845,14 +2914,12 @@ is a shorthand for (NAME NAME)."
(memq c2 (cl--struct-all-parents c1)))))))
(let ((c1 (and (symbolp t1) (cl--find-class t1))))
(and c1 (cl--struct-class-p c1)
- (funcall orig (if (eq 'list (cl-struct-sequence-type t1))
- 'consp 'vectorp)
+ (funcall orig (cl--defstruct-predicate t1)
pred2)))
(let ((c2 (and (symbolp t2) (cl--find-class t2))))
(and c2 (cl--struct-class-p c2)
(funcall orig pred1
- (if (eq 'list (cl-struct-sequence-type t2))
- 'consp 'vectorp))))
+ (cl--defstruct-predicate t2))))
(funcall orig pred1 pred2))))
(advice-add 'pcase--mutually-exclusive-p
:around #'cl--pcase-mutually-exclusive-p)
@@ -2860,8 +2927,8 @@ is a shorthand for (NAME NAME)."
(defun cl-struct-sequence-type (struct-type)
"Return the sequence used to build STRUCT-TYPE.
-STRUCT-TYPE is a symbol naming a struct type. Return `vector' or
-`list', or nil if STRUCT-TYPE is not a struct type. "
+STRUCT-TYPE is a symbol naming a struct type. Return `record',
+`vector`, or `list' if STRUCT-TYPE is a struct type, nil otherwise."
(declare (side-effect-free t) (pure t))
(cl--struct-class-type (cl--struct-get-class struct-type)))
@@ -3003,7 +3070,7 @@ omitted, a default message listing FORM itself is used."
(delq nil (mapcar (lambda (x)
(unless (macroexp-const-p x)
x))
- (cdr form))))))
+ (cdr-safe form))))))
`(progn
(or ,form
(cl--assertion-failed
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index 4ae77a58ec9..ab6354de7cd 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -64,7 +64,7 @@
;; cl--slot-descriptor.
;; BEWARE: Obviously, it's important to keep the two in sync!
(lambda (name &optional initform type props)
- (vector 'cl-struct-cl-slot-descriptor
+ (record 'cl-slot-descriptor
name initform type props)))
(defun cl--struct-get-class (name)
@@ -101,7 +101,7 @@
(defun cl--struct-register-child (parent tag)
;; Can't use (cl-typep parent 'cl-structure-class) at this stage
;; because `cl-structure-class' is defined later.
- (while (vectorp parent)
+ (while (recordp parent)
(add-to-list (cl--struct-class-children-sym parent) tag)
;; Only register ourselves as a child of the leftmost parent since structs
;; can only only have one parent.
@@ -110,6 +110,12 @@
;;;###autoload
(defun cl-struct-define (name docstring parent type named slots children-sym
tag print)
+ (unless type
+ ;; Legacy defstruct, using tagged vectors. Enable backward compatibility.
+ (cl-old-struct-compat-mode 1))
+ (if (eq type 'record)
+ ;; Defstruct using record objects.
+ (setq type nil))
(cl-assert (or type (not named)))
(if (boundp children-sym)
(add-to-list children-sym tag)
@@ -150,8 +156,21 @@
parent name))))
(add-to-list 'current-load-list `(define-type . ,name))
(cl--struct-register-child parent-class tag)
- (unless (eq named t)
- (eval `(defconst ,tag ',class) t)
+ (unless (or (eq named t) (eq tag name))
+ ;; We used to use `defconst' instead of `set' but that
+ ;; has a side-effect of purecopying during the dump, so that the
+ ;; class object stored in the tag ends up being a *copy* of the
+ ;; one stored in the `cl--class' property! We could have fixed
+ ;; this needless duplication by using the purecopied object, but
+ ;; that then breaks down a bit later when we modify the
+ ;; cl-structure-class class object to close the recursion
+ ;; between cl-structure-object and cl-structure-class (because
+ ;; modifying purecopied objects is not allowed. Since this is
+ ;; done during dumping, we could relax this rule and allow the
+ ;; modification, but it's cumbersome).
+ ;; So in the end, it's easier to just avoid the duplication by
+ ;; avoiding the use of the purespace here.
+ (set tag class)
;; In the cl-generic support, we need to be able to check
;; if a vector is a cl-struct object, without knowing its particular type.
;; So we use the (otherwise) unused function slots of the tag symbol
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
new file mode 100644
index 00000000000..70ccaac17b3
--- /dev/null
+++ b/lisp/emacs-lisp/cl-print.el
@@ -0,0 +1,244 @@
+;;; cl-print.el --- CL-style generic printing -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; Keywords:
+;; Version: 1.0
+;; Package-Requires: ((emacs "25"))
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Customizable print facility.
+;;
+;; The heart of it is the generic function `cl-print-object' to which you
+;; can add any method you like.
+;;
+;; The main entry point is `cl-prin1'.
+
+;;; Code:
+
+(defvar cl-print-readably nil
+ "If non-nil, try and make sure the result can be `read'.")
+
+(defvar cl-print--number-table nil)
+(defvar cl-print--currently-printing nil)
+
+;;;###autoload
+(cl-defgeneric cl-print-object (object stream)
+ "Dispatcher to print OBJECT on STREAM according to its type.
+You can add methods to it to customize the output.
+But if you just want to print something, don't call this directly:
+call other entry points instead, such as `cl-prin1'."
+ ;; This delegates to the C printer. The C printer will not call us back, so
+ ;; we should only use it for objects which don't have nesting.
+ (prin1 object stream))
+
+(cl-defmethod cl-print-object ((object cons) stream)
+ (let ((car (pop object)))
+ (if (and (memq car '(\, quote \` \,@ \,.))
+ (consp object)
+ (null (cdr object)))
+ (progn
+ (princ (if (eq car 'quote) '\' car) stream)
+ (cl-print-object (car object) stream))
+ (princ "(" stream)
+ (cl-print-object car stream)
+ (while (and (consp object)
+ (not (if cl-print--number-table
+ (numberp (gethash object cl-print--number-table))
+ (memq object cl-print--currently-printing))))
+ (princ " " stream)
+ (cl-print-object (pop object) stream))
+ (when object
+ (princ " . " stream) (cl-print-object object stream))
+ (princ ")" stream))))
+
+(cl-defmethod cl-print-object ((object vector) stream)
+ (princ "[" stream)
+ (dotimes (i (length object))
+ (unless (zerop i) (princ " " stream))
+ (cl-print-object (aref object i) stream))
+ (princ "]" stream))
+
+(defvar cl-print-compiled nil
+ "Control how to print byte-compiled functions. Can be:
+- `static' to print the vector of constants.
+- `disassemble' to print the disassembly of the code.
+- nil to skip printing any details about the code.")
+
+(cl-defmethod cl-print-object ((object compiled-function) stream)
+ ;; We use "#f(...)" rather than "#<...>" so that pp.el gives better results.
+ (princ "#f(compiled-function " stream)
+ (let ((args (help-function-arglist object 'preserve-names)))
+ (if args
+ (prin1 args stream)
+ (princ "()" stream)))
+ (let ((doc (documentation object 'raw)))
+ (when doc
+ (princ " " stream)
+ (prin1 doc stream)))
+ (let ((inter (interactive-form object)))
+ (when inter
+ (princ " " stream)
+ (cl-print-object
+ (if (eq 'byte-code (car-safe (cadr inter)))
+ `(interactive ,(make-byte-code nil (nth 1 (cadr inter))
+ (nth 2 (cadr inter))
+ (nth 3 (cadr inter))))
+ inter)
+ stream)))
+ (if (eq cl-print-compiled 'disassemble)
+ (princ
+ (with-temp-buffer
+ (insert "\n")
+ (disassemble-1 object 0)
+ (buffer-string))
+ stream)
+ (princ " #<bytecode>" stream)
+ (when (eq cl-print-compiled 'static)
+ (princ " " stream)
+ (cl-print-object (aref object 2) stream)))
+ (princ ")" stream))
+
+;; This belongs in nadvice.el, of course, but some load-ordering issues make it
+;; complicated: cl-generic uses macros from cl-macs and cl-macs uses advice-add
+;; from nadvice, so nadvice needs to be loaded before cl-generic and hence
+;; can't use cl-defmethod.
+(cl-defmethod cl-print-object :extra "nadvice"
+ ((object compiled-function) stream)
+ (if (not (advice--p object))
+ (cl-call-next-method)
+ (princ "#f(advice-wrapper " stream)
+ (when (fboundp 'advice--where)
+ (princ (advice--where object) stream)
+ (princ " " stream))
+ (cl-print-object (advice--cdr object) stream)
+ (princ " " stream)
+ (cl-print-object (advice--car object) stream)
+ (let ((props (advice--props object)))
+ (when props
+ (princ " " stream)
+ (cl-print-object props stream)))
+ (princ ")" stream)))
+
+(cl-defmethod cl-print-object ((object cl-structure-object) stream)
+ (princ "#s(" stream)
+ (let* ((class (cl-find-class (type-of object)))
+ (slots (cl--struct-class-slots class)))
+ (princ (cl--struct-class-name class) stream)
+ (dotimes (i (length slots))
+ (let ((slot (aref slots i)))
+ (princ " :" stream)
+ (princ (cl--slot-descriptor-name slot) stream)
+ (princ " " stream)
+ (cl-print-object (aref object (1+ i)) stream))))
+ (princ ")" stream))
+
+;;; Circularity and sharing.
+
+;; I don't try to support the `print-continuous-numbering', because
+;; I think it's ill defined anyway: if an object appears only once in each call
+;; its sharing can't be properly preserved!
+
+(cl-defmethod cl-print-object :around (object stream)
+ ;; FIXME: Only put such an :around method on types where it's relevant.
+ (cond
+ (print-circle
+ (let ((n (gethash object cl-print--number-table)))
+ (if (not (numberp n))
+ (cl-call-next-method)
+ (if (> n 0)
+ ;; Already printed. Just print a reference.
+ (progn (princ "#" stream) (princ n stream) (princ "#" stream))
+ (puthash object (- n) cl-print--number-table)
+ (princ "#" stream) (princ (- n) stream) (princ "=" stream)
+ (cl-call-next-method)))))
+ ((let ((already-printing (memq object cl-print--currently-printing)))
+ (when already-printing
+ ;; Currently printing, just print reference to avoid endless
+ ;; recursion.
+ (princ "#" stream)
+ (princ (length (cdr already-printing)) stream))))
+ (t (let ((cl-print--currently-printing
+ (cons object cl-print--currently-printing)))
+ (cl-call-next-method)))))
+
+(defvar cl-print--number-index nil)
+
+(defun cl-print--find-sharing (object table)
+ ;; Avoid recursion: not only because it's too easy to bump into
+ ;; `max-lisp-eval-depth', but also because function calls are fairly slow.
+ ;; At first, I thought using a list for our stack would cause too much
+ ;; garbage to generated, but I didn't notice any such problem in practice.
+ ;; I experimented with using an array instead, but the result was slightly
+ ;; slower and the reduction in GC activity was less than 1% on my test.
+ (let ((stack (list object)))
+ (while stack
+ (let ((object (pop stack)))
+ (unless
+ ;; Skip objects which don't have identity!
+ (or (floatp object) (numberp object)
+ (null object) (if (symbolp object) (intern-soft object)))
+ (let ((n (gethash object table)))
+ (cond
+ ((numberp n)) ;All done.
+ (n ;Already seen, but only once.
+ (let ((n (1+ cl-print--number-index)))
+ (setq cl-print--number-index n)
+ (puthash object (- n) table)))
+ (t
+ (puthash object t table)
+ (pcase object
+ (`(,car . ,cdr)
+ (push cdr stack)
+ (push car stack))
+ ((pred stringp)
+ ;; We presumably won't print its text-properties.
+ nil)
+ ((or (pred arrayp) (pred byte-code-function-p))
+ ;; FIXME: Inefficient for char-tables!
+ (dotimes (i (length object))
+ (push (aref object i) stack))))))))))))
+
+(defun cl-print--preprocess (object)
+ (let ((print-number-table (make-hash-table :test 'eq :rehash-size 2.0)))
+ (if (fboundp 'print--preprocess)
+ ;; Use the predefined C version if available.
+ (print--preprocess object) ;Fill print-number-table!
+ (let ((cl-print--number-index 0))
+ (cl-print--find-sharing object print-number-table)))
+ print-number-table))
+
+;;;###autoload
+(defun cl-prin1 (object &optional stream)
+ (cond
+ (cl-print-readably (prin1 object stream))
+ ((not print-circle) (cl-print-object object stream))
+ (t
+ (let ((cl-print--number-table (cl-print--preprocess object)))
+ (cl-print-object object stream)))))
+
+;;;###autoload
+(defun cl-prin1-to-string (object)
+ (with-temp-buffer
+ (cl-prin1 object (current-buffer))
+ (buffer-string)))
+
+(provide 'cl-print)
+;;; cl-print.el ends here
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index 5fc9eb1d9af..67ff1a00bd3 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -116,6 +116,16 @@
(defun cl-reduce (cl-func cl-seq &rest cl-keys)
"Reduce two-argument FUNCTION across SEQ.
\nKeywords supported: :start :end :from-end :initial-value :key
+
+Return the result of calling FUNCTION with the first and the
+second element of SEQ, then calling FUNCTION with that result and
+the third element of SEQ, then with that result and the fourth
+element of SEQ, etc.
+
+If :INITIAL-VALUE is specified, it is added to the front of SEQ.
+If SEQ is empty, return :INITIAL-VALUE and FUNCTION is not
+called.
+
\n(fn FUNCTION SEQ [KEYWORD VALUE]...)"
(cl--parsing-keywords (:from-end (:start 0) :end :initial-value :key) ()
(or (listp cl-seq) (setq cl-seq (append cl-seq nil)))
@@ -134,24 +144,24 @@
cl-accum)))
;;;###autoload
-(defun cl-fill (seq item &rest cl-keys)
+(defun cl-fill (cl-seq cl-item &rest cl-keys)
"Fill the elements of SEQ with ITEM.
\nKeywords supported: :start :end
\n(fn SEQ ITEM [KEYWORD VALUE]...)"
(cl--parsing-keywords ((:start 0) :end) ()
- (if (listp seq)
- (let ((p (nthcdr cl-start seq))
- (n (if cl-end (- cl-end cl-start) 8000000)))
- (while (and p (>= (setq n (1- n)) 0))
- (setcar p item)
+ (if (listp cl-seq)
+ (let ((p (nthcdr cl-start cl-seq))
+ (n (and cl-end (- cl-end cl-start))))
+ (while (and p (or (null n) (>= (cl-decf n) 0)))
+ (setcar p cl-item)
(setq p (cdr p))))
- (or cl-end (setq cl-end (length seq)))
- (if (and (= cl-start 0) (= cl-end (length seq)))
- (fillarray seq item)
+ (or cl-end (setq cl-end (length cl-seq)))
+ (if (and (= cl-start 0) (= cl-end (length cl-seq)))
+ (fillarray cl-seq cl-item)
(while (< cl-start cl-end)
- (aset seq cl-start item)
+ (aset cl-seq cl-start cl-item)
(setq cl-start (1+ cl-start)))))
- seq))
+ cl-seq))
;;;###autoload
(defun cl-replace (cl-seq1 cl-seq2 &rest cl-keys)
@@ -170,16 +180,20 @@ SEQ1 is destructively modified, then returned.
(elt cl-seq2 (+ cl-start2 cl-n))))))
(if (listp cl-seq1)
(let ((cl-p1 (nthcdr cl-start1 cl-seq1))
- (cl-n1 (if cl-end1 (- cl-end1 cl-start1) 4000000)))
+ (cl-n1 (and cl-end1 (- cl-end1 cl-start1))))
(if (listp cl-seq2)
(let ((cl-p2 (nthcdr cl-start2 cl-seq2))
- (cl-n (min cl-n1
- (if cl-end2 (- cl-end2 cl-start2) 4000000))))
- (while (and cl-p1 cl-p2 (>= (setq cl-n (1- cl-n)) 0))
+ (cl-n (cond ((and cl-n1 cl-end2)
+ (min cl-n1 (- cl-end2 cl-start2)))
+ ((and cl-n1 (null cl-end2)) cl-n1)
+ ((and (null cl-n1) cl-end2) (- cl-end2 cl-start2)))))
+ (while (and cl-p1 cl-p2 (or (null cl-n) (>= (cl-decf cl-n) 0)))
(setcar cl-p1 (car cl-p2))
(setq cl-p1 (cdr cl-p1) cl-p2 (cdr cl-p2))))
- (setq cl-end2 (min (or cl-end2 (length cl-seq2))
- (+ cl-start2 cl-n1)))
+ (setq cl-end2 (if (null cl-n1)
+ (or cl-end2 (length cl-seq2))
+ (min (or cl-end2 (length cl-seq2))
+ (+ cl-start2 cl-n1))))
(while (and cl-p1 (< cl-start2 cl-end2))
(setcar cl-p1 (aref cl-seq2 cl-start2))
(setq cl-p1 (cdr cl-p1) cl-start2 (1+ cl-start2)))))
@@ -205,9 +219,10 @@ to avoid corrupting the original SEQ.
\n(fn ITEM SEQ [KEYWORD VALUE]...)"
(cl--parsing-keywords (:test :test-not :key :if :if-not :count :from-end
(:start 0) :end) ()
- (if (<= (or cl-count (setq cl-count 8000000)) 0)
+ (let ((len (length cl-seq)))
+ (if (<= (or cl-count (setq cl-count len)) 0)
cl-seq
- (if (or (nlistp cl-seq) (and cl-from-end (< cl-count 4000000)))
+ (if (or (nlistp cl-seq) (and cl-from-end (< cl-count (/ len 2))))
(let ((cl-i (cl--position cl-item cl-seq cl-start cl-end
cl-from-end)))
(if cl-i
@@ -219,7 +234,7 @@ to avoid corrupting the original SEQ.
(if (listp cl-seq) cl-res
(if (stringp cl-seq) (concat cl-res) (vconcat cl-res))))
cl-seq))
- (setq cl-end (- (or cl-end 8000000) cl-start))
+ (setq cl-end (- (or cl-end len) cl-start))
(if (= cl-start 0)
(while (and cl-seq (> cl-end 0)
(cl--check-test cl-item (car cl-seq))
@@ -240,7 +255,7 @@ to avoid corrupting the original SEQ.
:start 0 :end (1- cl-end)
:count (1- cl-count) cl-keys))))
cl-seq))
- cl-seq)))))
+ cl-seq))))))
;;;###autoload
(defun cl-remove-if (cl-pred cl-list &rest cl-keys)
@@ -268,20 +283,21 @@ This is a destructive function; it reuses the storage of SEQ whenever possible.
\n(fn ITEM SEQ [KEYWORD VALUE]...)"
(cl--parsing-keywords (:test :test-not :key :if :if-not :count :from-end
(:start 0) :end) ()
- (if (<= (or cl-count (setq cl-count 8000000)) 0)
+ (let ((len (length cl-seq)))
+ (if (<= (or cl-count (setq cl-count len)) 0)
cl-seq
(if (listp cl-seq)
- (if (and cl-from-end (< cl-count 4000000))
+ (if (and cl-from-end (< cl-count (/ len 2)))
(let (cl-i)
(while (and (>= (setq cl-count (1- cl-count)) 0)
(setq cl-i (cl--position cl-item cl-seq cl-start
- cl-end cl-from-end)))
+ cl-end cl-from-end)))
(if (= cl-i 0) (setq cl-seq (cdr cl-seq))
(let ((cl-tail (nthcdr (1- cl-i) cl-seq)))
(setcdr cl-tail (cdr (cdr cl-tail)))))
(setq cl-end cl-i))
cl-seq)
- (setq cl-end (- (or cl-end 8000000) cl-start))
+ (setq cl-end (- (or cl-end len) cl-start))
(if (= cl-start 0)
(progn
(while (and cl-seq
@@ -302,7 +318,7 @@ This is a destructive function; it reuses the storage of SEQ whenever possible.
(setq cl-p (cdr cl-p)))
(setq cl-end (1- cl-end)))))
cl-seq)
- (apply 'cl-remove cl-item cl-seq cl-keys)))))
+ (apply 'cl-remove cl-item cl-seq cl-keys))))))
;;;###autoload
(defun cl-delete-if (cl-pred cl-list &rest cl-keys)
@@ -337,6 +353,7 @@ This is a destructive function; it reuses the storage of SEQ whenever possible.
(defun cl--delete-duplicates (cl-seq cl-keys cl-copy)
(if (listp cl-seq)
(cl--parsing-keywords
+ ;; We need to parse :if, otherwise `cl-if' is unbound.
(:test :test-not :key (:start 0) :end :from-end :if)
()
(if cl-from-end
@@ -385,15 +402,17 @@ to avoid corrupting the original SEQ.
(cl--parsing-keywords (:test :test-not :key :if :if-not :count
(:start 0) :end :from-end) ()
(if (or (eq cl-old cl-new)
- (<= (or cl-count (setq cl-from-end nil cl-count 8000000)) 0))
+ (<= (or cl-count (setq cl-from-end nil
+ cl-count (length cl-seq))) 0))
cl-seq
(let ((cl-i (cl--position cl-old cl-seq cl-start cl-end)))
(if (not cl-i)
cl-seq
(setq cl-seq (copy-sequence cl-seq))
- (or cl-from-end
- (progn (setf (elt cl-seq cl-i) cl-new)
- (setq cl-i (1+ cl-i) cl-count (1- cl-count))))
+ (unless cl-from-end
+ (setf (elt cl-seq cl-i) cl-new)
+ (cl-incf cl-i)
+ (cl-decf cl-count))
(apply 'cl-nsubstitute cl-new cl-old cl-seq :count cl-count
:start cl-i cl-keys))))))
@@ -423,17 +442,18 @@ This is a destructive function; it reuses the storage of SEQ whenever possible.
\n(fn NEW OLD SEQ [KEYWORD VALUE]...)"
(cl--parsing-keywords (:test :test-not :key :if :if-not :count
(:start 0) :end :from-end) ()
- (or (eq cl-old cl-new) (<= (or cl-count (setq cl-count 8000000)) 0)
- (if (and (listp cl-seq) (or (not cl-from-end) (> cl-count 4000000)))
+ (let ((len (length cl-seq)))
+ (or (eq cl-old cl-new) (<= (or cl-count (setq cl-count len)) 0)
+ (if (and (listp cl-seq) (or (not cl-from-end) (> cl-count (/ len 2))))
(let ((cl-p (nthcdr cl-start cl-seq)))
- (setq cl-end (- (or cl-end 8000000) cl-start))
+ (setq cl-end (- (or cl-end len) cl-start))
(while (and cl-p (> cl-end 0) (> cl-count 0))
(if (cl--check-test cl-old (car cl-p))
(progn
(setcar cl-p cl-new)
(setq cl-count (1- cl-count))))
(setq cl-p (cdr cl-p) cl-end (1- cl-end))))
- (or cl-end (setq cl-end (length cl-seq)))
+ (or cl-end (setq cl-end len))
(if cl-from-end
(while (and (< cl-start cl-end) (> cl-count 0))
(setq cl-end (1- cl-end))
@@ -446,7 +466,7 @@ This is a destructive function; it reuses the storage of SEQ whenever possible.
(progn
(aset cl-seq cl-start cl-new)
(setq cl-count (1- cl-count))))
- (setq cl-start (1+ cl-start))))))
+ (setq cl-start (1+ cl-start)))))))
cl-seq))
;;;###autoload
@@ -502,14 +522,13 @@ Return the index of the matching item, or nil if not found.
(defun cl--position (cl-item cl-seq cl-start &optional cl-end cl-from-end)
(if (listp cl-seq)
- (let ((cl-p (nthcdr cl-start cl-seq)))
- (or cl-end (setq cl-end 8000000))
- (let ((cl-res nil))
- (while (and cl-p (< cl-start cl-end) (or (not cl-res) cl-from-end))
+ (let ((cl-p (nthcdr cl-start cl-seq))
+ cl-res)
+ (while (and cl-p (or (null cl-end) (< cl-start cl-end)) (or (null cl-res) cl-from-end))
(if (cl--check-test cl-item (car cl-p))
(setq cl-res cl-start))
(setq cl-p (cdr cl-p) cl-start (1+ cl-start)))
- cl-res))
+ cl-res)
(or cl-end (setq cl-end (length cl-seq)))
(if cl-from-end
(progn
diff --git a/lisp/emacs-lisp/cl.el b/lisp/emacs-lisp/cl.el
index a37faf99114..73eb9a4e866 100644
--- a/lisp/emacs-lisp/cl.el
+++ b/lisp/emacs-lisp/cl.el
@@ -154,7 +154,6 @@
every
some
mapcon
- mapcan
mapl
maplist
map
@@ -259,30 +258,6 @@
copy-list
ldiff
list*
- cddddr
- cdddar
- cddadr
- cddaar
- cdaddr
- cdadar
- cdaadr
- cdaaar
- cadddr
- caddar
- cadadr
- cadaar
- caaddr
- caadar
- caaadr
- caaaar
- cdddr
- cddar
- cdadr
- cdaar
- caddr
- cadar
- caadr
- caaar
tenth
ninth
eighth
@@ -365,7 +340,7 @@ The two cases that are handled are:
`(list 'lambda '(&rest --cl-rest--)
,@(cl-sublis sub (nreverse decls))
(list 'apply
- (list 'quote
+ (list 'function
#'(lambda ,(append new (cadr f))
,@(cl-sublis sub body)))
,@(nconc (mapcar (lambda (x) `(list 'quote ,x))
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index c969b8253fe..83456fc31a2 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -274,13 +274,14 @@ That buffer should be current already."
(let ((standard-output (current-buffer))
(print-escape-newlines t)
(print-level 8)
- (print-length 50))
- (backtrace))
+ (print-length 50))
+ ;; FIXME the debugger could pass a custom callback to mapbacktrace
+ ;; instead of manipulating printed results.
+ (mapbacktrace #'backtrace--print-frame 'debug))
(goto-char (point-min))
(delete-region (point)
(progn
- (search-forward "\n debug(")
- (forward-line (if (eq (car args) 'debug)
+ (forward-line (if (eq (car args) 'debug)
;; Remove debug--implement-debug-on-entry
;; and the advice's `apply' frame.
3
@@ -304,6 +305,24 @@ That buffer should be current already."
(delete-char 1)
(insert ? )
(beginning-of-line))
+ ;; Watchpoint triggered.
+ ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args)))
+ (insert
+ "--"
+ (pcase details
+ (`(makunbound nil) (format "making %s void" symbol))
+ (`(makunbound ,buffer) (format "killing local value of %s in buffer %s"
+ symbol buffer))
+ (`(defvaralias ,_) (format "aliasing %s to %s" symbol newval))
+ (`(let ,_) (format "let-binding %s to %S" symbol newval))
+ (`(unlet ,_) (format "ending let-binding of %s" symbol))
+ (`(set nil) (format "setting %s to %S" symbol newval))
+ (`(set ,buffer) (format "setting %s in buffer %s to %S"
+ symbol buffer newval))
+ (_ (error "unrecognized watchpoint triggered %S" (cdr args))))
+ ": ")
+ (setq pos (point))
+ (insert ?\n))
;; Debugger entered for an error.
(`error
(insert "--Lisp error: ")
@@ -708,6 +727,9 @@ Complete list of commands:
\\{debugger-mode-map}"
(setq truncate-lines t)
(set-syntax-table emacs-lisp-mode-syntax-table)
+ (add-hook 'kill-buffer-hook
+ (lambda () (if (> (recursion-depth) 0) (top-level)))
+ nil t)
(use-local-map debugger-mode-map))
(defcustom debugger-record-buffer "*Debugger-record*"
@@ -848,6 +870,79 @@ To specify a nil argument interactively, exit with an empty minibuffer."
(princ "Note: if you have redefined a function, then it may no longer\n")
(princ "be set to debug on entry, even if it is in the list."))))))
+(defun debug--implement-debug-watch (symbol newval op where)
+ "Conditionally call the debugger.
+This function is called when SYMBOL's value is modified."
+ (if (or inhibit-debug-on-entry debugger-jumping-flag)
+ nil
+ (let ((inhibit-debug-on-entry t))
+ (funcall debugger 'watchpoint symbol newval op where))))
+
+;;;###autoload
+(defun debug-on-variable-change (variable)
+ "Trigger a debugger invocation when VARIABLE is changed.
+
+When called interactively, prompt for VARIABLE in the minibuffer.
+
+This works by calling `add-variable-watch' on VARIABLE. If you
+quit from the debugger, this will abort the change (unless the
+change is caused by the termination of a let-binding).
+
+The watchpoint may be circumvented by C code that changes the
+variable directly (i.e., not via `set'). Changing the value of
+the variable (e.g., `setcar' on a list variable) will not trigger
+watchpoint.
+
+Use \\[cancel-debug-on-variable-change] to cancel the effect of
+this command. Uninterning VARIABLE or making it an alias of
+another symbol also cancels it."
+ (interactive
+ (let* ((var-at-point (variable-at-point))
+ (var (and (symbolp var-at-point) var-at-point))
+ (val (completing-read
+ (concat "Debug when setting variable"
+ (if var (format " (default %s): " var) ": "))
+ obarray #'boundp
+ t nil nil (and var (symbol-name var)))))
+ (list (if (equal val "") var (intern val)))))
+ (add-variable-watcher variable #'debug--implement-debug-watch))
+
+;;;###autoload
+(defalias 'debug-watch #'debug-on-variable-change)
+
+
+(defun debug--variable-list ()
+ "List of variables currently set for debug on set."
+ (let ((vars '()))
+ (mapatoms
+ (lambda (s)
+ (when (memq #'debug--implement-debug-watch
+ (get s 'watchers))
+ (push s vars))))
+ vars))
+
+;;;###autoload
+(defun cancel-debug-on-variable-change (&optional variable)
+ "Undo effect of \\[debug-on-variable-change] on VARIABLE.
+If VARIABLE is nil, cancel debug-on-variable-change for all variables.
+When called interactively, prompt for VARIABLE in the minibuffer.
+To specify a nil argument interactively, exit with an empty minibuffer."
+ (interactive
+ (list (let ((name
+ (completing-read
+ "Cancel debug on set for variable (default all variables): "
+ (mapcar #'symbol-name (debug--variable-list)) nil t)))
+ (when name
+ (unless (string= name "")
+ (intern name))))))
+ (if variable
+ (remove-variable-watcher variable #'debug--implement-debug-watch)
+ (message "Canceling debug-watch for all variables")
+ (mapc #'cancel-debug-watch (debug--variable-list))))
+
+;;;###autoload
+(defalias 'cancel-debug-watch #'cancel-debug-on-variable-change)
+
(provide 'debug)
;;; debug.el ends here
diff --git a/lisp/emacs-lisp/derived.el b/lisp/emacs-lisp/derived.el
index c73d2f0bf63..fffe972460c 100644
--- a/lisp/emacs-lisp/derived.el
+++ b/lisp/emacs-lisp/derived.el
@@ -137,6 +137,9 @@ BODY can start with a bunch of keyword arguments. The following keyword
:abbrev-table TABLE
Use TABLE instead of the default (CHILD-abbrev-table).
A nil value means to simply use the same abbrev-table as the parent.
+:after-hook FORM
+ A single lisp form which is evaluated after the mode hooks have been
+ run. It should not be quoted.
Here is how you could define LaTeX-Thesis mode as a variant of LaTeX mode:
@@ -184,7 +187,8 @@ See Info node `(elisp)Derived Modes' for more details."
(declare-abbrev t)
(declare-syntax t)
(hook (derived-mode-hook-name child))
- (group nil))
+ (group nil)
+ (after-hook nil))
;; Process the keyword args.
(while (keywordp (car body))
@@ -192,6 +196,7 @@ See Info node `(elisp)Derived Modes' for more details."
(`:group (setq group (pop body)))
(`:abbrev-table (setq abbrev (pop body)) (setq declare-abbrev nil))
(`:syntax-table (setq syntax (pop body)) (setq declare-syntax nil))
+ (`:after-hook (setq after-hook (pop body)))
(_ (pop body))))
(setq docstring (derived-mode-make-docstring
@@ -211,17 +216,20 @@ No problems result if this variable is not bound.
(purecopy ,(format "Keymap for `%s'." child))))
,(if declare-syntax
`(progn
+ (defvar ,syntax)
(unless (boundp ',syntax)
- (put ',syntax 'definition-name ',child))
- (defvar ,syntax (make-syntax-table))
+ (put ',syntax 'definition-name ',child)
+ (defvar ,syntax (make-syntax-table)))
(unless (get ',syntax 'variable-documentation)
(put ',syntax 'variable-documentation
(purecopy ,(format "Syntax table for `%s'." child))))))
,(if declare-abbrev
`(progn
- (put ',abbrev 'definition-name ',child)
- (defvar ,abbrev
- (progn (define-abbrev-table ',abbrev nil) ,abbrev))
+ (defvar ,abbrev)
+ (unless (boundp ',abbrev)
+ (put ',abbrev 'definition-name ',child)
+ (defvar ,abbrev
+ (progn (define-abbrev-table ',abbrev nil) ,abbrev)))
(unless (get ',abbrev 'variable-documentation)
(put ',abbrev 'variable-documentation
(purecopy ,(format "Abbrev table for `%s'." child))))))
@@ -272,7 +280,11 @@ No problems result if this variable is not bound.
,@body
)
;; Run the hooks, if any.
- (run-mode-hooks ',hook)))))
+ (run-mode-hooks ',hook)
+ ,@(when after-hook
+ `((if delay-mode-hooks
+ (push ',after-hook delayed-after-hook-forms)
+ ,after-hook)))))))
;; PUBLIC: find the ultimate class of a derived mode.
@@ -344,7 +356,7 @@ which more-or-less shadow%s %s's corresponding table%s."
(format "`%s' " parent))
"might have run,\nthis mode "))
(format "runs the hook `%s'" hook)
- ", as the final step\nduring initialization.")))
+ ", as the final or penultimate step\nduring initialization.")))
(unless (string-match "\\\\[{[]" docstring)
;; And don't forget to put the mode's keymap.
diff --git a/lisp/emacs-lisp/disass.el b/lisp/emacs-lisp/disass.el
index 97e45e070d0..66673b4d26c 100644
--- a/lisp/emacs-lisp/disass.el
+++ b/lisp/emacs-lisp/disass.el
@@ -221,9 +221,21 @@ OBJ should be a call to BYTE-CODE generated by the byte compiler."
((memq op '(byte-constant byte-constant2))
;; it's a constant
(setq arg (car arg))
- ;; but if the value of the constant is compiled code, then
- ;; recursively disassemble it.
- (cond ((or (byte-code-function-p arg)
+ ;; if the succeeding op is byte-switch, display the jump table
+ ;; used
+ (cond ((eq (car-safe (car-safe (cdr lap))) 'byte-switch)
+ (insert (format "<jump-table-%s (" (hash-table-test arg)))
+ (let ((first-time t))
+ (maphash #'(lambda (value tag)
+ (if first-time
+ (setq first-time nil)
+ (insert " "))
+ (insert (format "%s %s" value (cadr tag))))
+ arg))
+ (insert ")>"))
+ ;; if the value of the constant is compiled code, then
+ ;; recursively disassemble it.
+ ((or (byte-code-function-p arg)
(and (consp arg) (functionp arg)
(assq 'byte-code arg))
(and (eq (car-safe arg) 'macro)
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index df1f893288c..65e30f86778 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -112,6 +112,18 @@ and some not, use `def-edebug-spec' to specify an `edebug-form-spec'."
:type 'boolean
:group 'edebug)
+(defcustom edebug-max-depth 150
+ "Maximum recursion depth when instrumenting code.
+This limit is intended to stop recursion if an Edebug specification
+contains an infinite loop. When Edebug is instrumenting code
+containing very large quoted lists, it may reach this limit and give
+the error message \"Too deep - perhaps infinite loop in spec?\".
+Make this limit larger to countermand that, but you may also need to
+increase `max-lisp-eval-depth' and `max-specpdl-size'."
+ :type 'integer
+ :group 'edebug
+ :version "26.1")
+
(defcustom edebug-save-windows t
"If non-nil, Edebug saves and restores the window configuration.
That takes some time, so if your program does not care what happens to
@@ -233,6 +245,12 @@ If the result is non-nil, then break. Errors are ignored."
:type 'number
:group 'edebug)
+(defcustom edebug-sit-on-break t
+ "Whether or not to pause for `edebug-sit-for-seconds' on reaching a break."
+ :type 'boolean
+ :group 'edebug
+ :version "26.1")
+
;;; Form spec utilities.
(defun get-edebug-spec (symbol)
@@ -380,31 +398,30 @@ Return the result of the last expression in BODY."
(defun edebug-current-windows (which-windows)
;; Get either a full window configuration or some window information.
(if (listp which-windows)
- (mapcar (function (lambda (window)
- (if (edebug-window-live-p window)
- (list window
- (window-buffer window)
- (window-point window)
- (window-start window)
- (window-hscroll window)))))
+ (mapcar (lambda (window)
+ (if (edebug-window-live-p window)
+ (list window
+ (window-buffer window)
+ (window-point window)
+ (window-start window)
+ (window-hscroll window))))
which-windows)
(current-window-configuration)))
(defun edebug-set-windows (window-info)
;; Set either a full window configuration or some window information.
(if (listp window-info)
- (mapcar (function
- (lambda (one-window-info)
- (if one-window-info
- (apply (function
- (lambda (window buffer point start hscroll)
- (if (edebug-window-live-p window)
- (progn
- (set-window-buffer window buffer)
- (set-window-point window point)
- (set-window-start window start)
- (set-window-hscroll window hscroll)))))
- one-window-info))))
+ (mapcar (lambda (one-window-info)
+ (if one-window-info
+ (apply (function
+ (lambda (window buffer point start hscroll)
+ (if (edebug-window-live-p window)
+ (progn
+ (set-window-buffer window buffer)
+ (set-window-point window point)
+ (set-window-start window start)
+ (set-window-hscroll window hscroll)))))
+ one-window-info)))
window-info)
(set-window-configuration window-info)))
@@ -640,7 +657,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
(progn
;; Instead of this, we could just find all contained forms.
;; (put (car entry) 'edebug nil) ;
- ;; (mapcar 'edebug-clear-form-data-entry ; dangerous
+ ;; (mapcar #'edebug-clear-form-data-entry ; dangerous
;; (get (car entry) 'edebug-dependents))
;; (set-marker (nth 1 entry) nil)
;; (set-marker (nth 2 entry) nil)
@@ -716,9 +733,9 @@ Maybe clear the markers and delete the symbol's edebug property?"
((eq class 'string) (read (current-buffer)))
((eq class 'quote) (forward-char 1)
(list 'quote (edebug-read-sexp)))
- ((eq class 'backquote)
+ ((eq class 'backquote) (forward-char 1)
(list '\` (edebug-read-sexp)))
- ((eq class 'comma)
+ ((eq class 'comma) (forward-char 1)
(list '\, (edebug-read-sexp)))
(t ; anything else, just read it.
(read (current-buffer))))))
@@ -737,6 +754,11 @@ Maybe clear the markers and delete the symbol's edebug property?"
(defvar edebug-offsets-stack nil)
(defvar edebug-current-offset nil) ; Top of the stack, for convenience.
+;; The association list of objects read with the #n=object form.
+;; Each member of the list has the form (n . object), and is used to
+;; look up the object for the corresponding #n# construct.
+(defvar edebug-read-objects nil)
+
;; We must store whether we just read a list with a dotted form that
;; is itself a list. This structure will be condensed, so the offsets
;; must also be condensed.
@@ -808,7 +830,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
(backquote . edebug-read-backquote)
(comma . edebug-read-comma)
(lbracket . edebug-read-vector)
- (hash . edebug-read-function)
+ (hash . edebug-read-special)
))
(defun edebug-read-storing-offsets (stream)
@@ -854,19 +876,47 @@ Maybe clear the markers and delete the symbol's edebug property?"
(edebug-storing-offsets opoint symbol)
(edebug-read-storing-offsets stream)))))
-(defun edebug-read-function (stream)
- ;; Turn #'thing into (function thing)
- (forward-char 1)
- (cond ((eq ?\' (following-char))
- (forward-char 1)
- (list
- (edebug-storing-offsets (- (point) 2) 'function)
- (edebug-read-storing-offsets stream)))
- ((memq (following-char) '(?: ?B ?O ?X ?b ?o ?x ?1 ?2 ?3 ?4 ?5 ?6
- ?7 ?8 ?9 ?0))
- (backward-char 1)
- (read stream))
- (t (edebug-syntax-error "Bad char after #"))))
+(defun edebug-read-special (stream)
+ "Read from STREAM a Lisp object beginning with #.
+Turn #'thing into (function thing) and handle the read syntax for
+circular objects. Let `read' read everything else."
+ (catch 'return
+ (forward-char 1)
+ (let ((start (point)))
+ (cond
+ ((eq ?\' (following-char))
+ (forward-char 1)
+ (throw 'return
+ (list
+ (edebug-storing-offsets (- (point) 2) 'function)
+ (edebug-read-storing-offsets stream))))
+ ((and (>= (following-char) ?0) (<= (following-char) ?9))
+ (while (and (>= (following-char) ?0) (<= (following-char) ?9))
+ (forward-char 1))
+ (let ((n (string-to-number (buffer-substring start (point)))))
+ (when (and read-circle
+ (<= n most-positive-fixnum))
+ (cond
+ ((eq ?= (following-char))
+ ;; Make a placeholder for #n# to use temporarily.
+ (let* ((placeholder (cons nil nil))
+ (elem (cons n placeholder)))
+ (push elem edebug-read-objects)
+ ;; Read the object and then replace the placeholder
+ ;; with the object itself, wherever it occurs.
+ (forward-char 1)
+ (let ((obj (edebug-read-storing-offsets stream)))
+ (substitute-object-in-subtree obj placeholder)
+ (throw 'return (setf (cdr elem) obj)))))
+ ((eq ?# (following-char))
+ ;; #n# returns a previously read object.
+ (let ((elem (assq n edebug-read-objects)))
+ (when (consp elem)
+ (forward-char 1)
+ (throw 'return (cdr elem))))))))))
+ ;; Let read handle errors, radix notation, and anything else.
+ (goto-char (1- start))
+ (read stream))))
(defun edebug-read-list (stream)
(forward-char 1) ; skip \(
@@ -894,7 +944,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
(let ((elements))
(while (not (eq 'rbracket (edebug-next-token-class)))
(push (edebug-read-storing-offsets stream) elements))
- (apply 'vector (nreverse elements)))
+ (apply #'vector (nreverse elements)))
(forward-char 1) ; skip \]
))
@@ -937,7 +987,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
;; Check if a dotted form is required.
(if edebug-dotted-spec (edebug-no-match cursor "Dot expected."))
;; Check if there is at least one more argument.
- (if (edebug-empty-cursor cursor) (apply 'edebug-no-match cursor error))
+ (if (edebug-empty-cursor cursor) (apply #'edebug-no-match cursor error))
;; Return that top element.
(edebug-top-element cursor))
@@ -1044,7 +1094,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
(setq result (edebug-read-and-maybe-wrap-form1))
nil)))
(if no-match
- (apply 'edebug-syntax-error no-match)))
+ (apply #'edebug-syntax-error no-match)))
result))
@@ -1058,6 +1108,7 @@ Maybe clear the markers and delete the symbol's edebug property?"
edebug-offsets
edebug-offsets-stack
edebug-current-offset ; reset to nil
+ edebug-read-objects
)
(save-excursion
(if (and (eq 'lparen (edebug-next-token-class))
@@ -1203,7 +1254,7 @@ expressions; a `progn' form will be returned enclosing these forms."
(setq sexp new-sexp
new-sexp (edebug-unwrap sexp)))
(if (consp new-sexp)
- (mapcar 'edebug-unwrap* new-sexp)
+ (mapcar #'edebug-unwrap* new-sexp)
new-sexp)))
@@ -1446,7 +1497,6 @@ expressions; a `progn' form will be returned enclosing these forms."
(defvar edebug-after-dotted-spec nil)
(defvar edebug-matching-depth 0) ;; initial value
-(defconst edebug-max-depth 150) ;; maximum number of matching recursions.
;;; Failure to match
@@ -1465,7 +1515,7 @@ expressions; a `progn' form will be returned enclosing these forms."
(progn
(if edebug-error-point
(goto-char edebug-error-point))
- (apply 'edebug-syntax-error args))
+ (apply #'edebug-syntax-error args))
(throw 'no-match args)))
@@ -1557,6 +1607,7 @@ expressions; a `progn' form will be returned enclosing these forms."
;; Less frequently used:
;; (function . edebug-match-function)
(lambda-expr . edebug-match-lambda-expr)
+ (cl-generic-method-args . edebug-match-cl-generic-method-args)
(&not . edebug-match-&not)
(&key . edebug-match-&key)
(place . edebug-match-place)
@@ -1661,7 +1712,7 @@ expressions; a `progn' form will be returned enclosing these forms."
;; Reset the cursor for the next match.
(edebug-set-cursor cursor this-form this-offset))
;; All failed.
- (apply 'edebug-no-match cursor "Expected one of" original-specs))
+ (apply #'edebug-no-match cursor "Expected one of" original-specs))
))
@@ -1687,9 +1738,9 @@ expressions; a `progn' form will be returned enclosing these forms."
(edebug-match-&rest
cursor
(cons '&or
- (mapcar (function (lambda (pair)
- (vector (format ":%s" (car pair))
- (car (cdr pair)))))
+ (mapcar (lambda (pair)
+ (vector (format ":%s" (car pair))
+ (car (cdr pair))))
specs))))
@@ -1734,7 +1785,7 @@ expressions; a `progn' form will be returned enclosing these forms."
form (cdr (edebug-top-offset cursor)))
(cdr specs))))
(edebug-move-cursor cursor)
- (list (apply 'vector result)))
+ (list (apply #'vector result)))
(edebug-no-match cursor "Expected" specs)))
((listp form)
@@ -1761,7 +1812,7 @@ expressions; a `progn' form will be returned enclosing these forms."
(edebug-match-specs cursor specs 'edebug-match-specs)
(if (not (edebug-empty-cursor cursor))
(if edebug-best-error
- (apply 'edebug-no-match cursor edebug-best-error)
+ (apply #'edebug-no-match cursor edebug-best-error)
;; A failed &rest or &optional spec may leave some args.
(edebug-no-match cursor "Failed matching" specs)
)))))
@@ -1850,6 +1901,16 @@ expressions; a `progn' form will be returned enclosing these forms."
spec))
nil)
+(defun edebug-match-cl-generic-method-args (cursor)
+ (let ((args (edebug-top-element-required cursor "Expected arguments")))
+ (if (not (consp args))
+ (edebug-no-match cursor "List expected"))
+ ;; Append the arguments to edebug-def-name.
+ (setq edebug-def-name
+ (intern (format "%s %s" edebug-def-name args)))
+ (edebug-move-cursor cursor)
+ (list args)))
+
(defun edebug-match-arg (cursor)
;; set the def-args bound in edebug-defining-form
(let ((edebug-arg (edebug-top-element-required cursor "Expected arg")))
@@ -1927,6 +1988,7 @@ expressions; a `progn' form will be returned enclosing these forms."
(def-edebug-spec defun
(&define name lambda-list
[&optional stringp]
+ [&optional ("declare" &rest sexp)]
[&optional ("interactive" interactive)]
def-body))
(def-edebug-spec defmacro
@@ -2163,8 +2225,7 @@ The purpose of this function is so you can properly undo
subsequent changes to the same binding, by passing the status
cons cell to `edebug-restore-status'. The status cons cell
has the form (LOCUS . VALUE), where LOCUS can be a buffer
-\(for a buffer-local binding), a frame (for a frame-local binding),
-or nil (if the default binding is current)."
+\(for a buffer-local binding), or nil (if the default binding is current)."
(cons (variable-binding-locus var)
(symbol-value var)))
@@ -2356,7 +2417,7 @@ MSG is printed after `::::} '."
(defvar edebug-window-data) ; window and window-start for current function
(defvar edebug-outside-windows) ; outside window configuration
(defvar edebug-eval-buffer) ; for the evaluation list.
-(defvar edebug-outside-d-c-i-n-s-w) ; outside default-cursor-in-non-selected-windows
+(defvar edebug-outside-d-c-i-n-s-w) ; outside default cursor-in-non-selected-windows
(defvar edebug-eval-list nil) ;; List of expressions to evaluate.
@@ -2489,6 +2550,7 @@ MSG is printed after `::::} '."
(progn
;; Display result of previous evaluation.
(if (and edebug-break
+ edebug-sit-on-break
(not (eq edebug-execution-mode 'Continue-fast)))
(sit-for edebug-sit-for-seconds)) ; Show message.
(edebug-previous-result)))
@@ -3135,8 +3197,11 @@ go to the end of the last sexp, or if that is the same point, then step."
)))))
(defun edebug-instrument-function (func)
- ;; Func should be a function symbol.
- ;; Return the function symbol, or nil if not instrumented.
+ "Instrument the function or generic method FUNC.
+Return the list of function symbols which were instrumented.
+This may be simply (FUNC) for a normal function, or a list of
+generated symbols for methods. If a function or method to
+instrument cannot be found, signal an error."
(let ((func-marker (get func 'edebug)))
(cond
((and (markerp func-marker) (marker-buffer func-marker))
@@ -3144,10 +3209,24 @@ go to the end of the last sexp, or if that is the same point, then step."
(with-current-buffer (marker-buffer func-marker)
(goto-char func-marker)
(edebug-eval-top-level-form)
- func))
+ (list func)))
((consp func-marker)
(message "%s is already instrumented." func)
- func)
+ (list func))
+ ((get func 'cl--generic)
+ (let ((method-defs (method-files func))
+ symbols)
+ (unless method-defs
+ (error "Could not find any method definitions for %s" func))
+ (pcase-dolist (`(,file . ,spec) method-defs)
+ (let* ((loc (find-function-search-for-symbol spec 'cl-defmethod file)))
+ (unless (cdr loc)
+ (error "Could not find the definition for %s in its file" spec))
+ (with-current-buffer (car loc)
+ (goto-char (cdr loc))
+ (edebug-eval-top-level-form)
+ (push (edebug-form-data-symbol) symbols))))
+ symbols))
(t
(let ((loc (find-function-noselect func t)))
(unless (cdr loc)
@@ -3155,13 +3234,16 @@ go to the end of the last sexp, or if that is the same point, then step."
(with-current-buffer (car loc)
(goto-char (cdr loc))
(edebug-eval-top-level-form)
- func))))))
+ (list func)))))))
(defun edebug-instrument-callee ()
"Instrument the definition of the function or macro about to be called.
Do this when stopped before the form or it will be too late.
One side effect of using this command is that the next time the
-function or macro is called, Edebug will be called there as well."
+function or macro is called, Edebug will be called there as well.
+If the callee is a generic function, Edebug will instrument all
+the methods, not just the one which is about to be called. Return
+the list of symbols which were instrumented."
(interactive)
(if (not (looking-at "("))
(error "You must be before a list form")
@@ -3176,15 +3258,15 @@ function or macro is called, Edebug will be called there as well."
(defun edebug-step-in ()
- "Step into the definition of the function or macro about to be called.
+ "Step into the definition of the function, macro or method about to be called.
This first does `edebug-instrument-callee' to ensure that it is
instrumented. Then it does `edebug-on-entry' and switches to `go' mode."
(interactive)
- (let ((func (edebug-instrument-callee)))
- (if func
+ (let ((funcs (edebug-instrument-callee)))
+ (if funcs
(progn
- (edebug-on-entry func 'temp)
- (edebug-go-mode nil)))))
+ (mapc (lambda (func) (edebug-on-entry func 'temp)) funcs)
+ (edebug-go-mode nil)))))
(defun edebug-on-entry (function &optional flag)
"Cause Edebug to stop when FUNCTION is called.
@@ -3325,10 +3407,10 @@ Return the result of the last expression."
(message "%s: %s"
(or (get (car value) 'error-message)
(format "peculiar error (%s)" (car value)))
- (mapconcat (function (lambda (edebug-arg)
- ;; continuing after an error may
- ;; complain about edebug-arg. why??
- (prin1-to-string edebug-arg)))
+ (mapconcat (lambda (edebug-arg)
+ ;; continuing after an error may
+ ;; complain about edebug-arg. why??
+ (prin1-to-string edebug-arg))
(cdr value) ", ")))
(defvar print-readably) ; defined by lemacs
@@ -3359,11 +3441,9 @@ Return the result of the last expression."
;;; Read, Eval and Print
-(defalias 'edebug-prin1 'prin1)
-(defalias 'edebug-print 'print)
-(defalias 'edebug-prin1-to-string 'prin1-to-string)
-(defalias 'edebug-format 'format-message)
-(defalias 'edebug-message 'message)
+(defalias 'edebug-prin1-to-string #'cl-prin1-to-string)
+(defalias 'edebug-format #'format-message)
+(defalias 'edebug-message #'message)
(defun edebug-eval-expression (expr)
"Evaluate an expression in the outside environment.
@@ -3604,7 +3684,7 @@ Options:
;; Don't do any edebug things now.
(let ((edebug-execution-mode 'Go-nonstop)
(edebug-trace nil))
- (mapcar 'edebug-safe-eval edebug-eval-list)))
+ (mapcar #'edebug-safe-eval edebug-eval-list)))
(defun edebug-eval-display-list (eval-result-list)
;; Assumes edebug-eval-buffer exists.
@@ -3752,7 +3832,7 @@ Otherwise call `debug' normally."
;; Otherwise call debug normally.
;; Still need to remove extraneous edebug calls from stack.
- (apply 'debug arg-mode args)
+ (apply #'debug arg-mode args)
))
@@ -3790,7 +3870,9 @@ Otherwise call `debug' normally."
(forward-line 1)
(delete-region last-ok-point (point)))
- ((looking-at "^ edebug")
+ ((looking-at (if debugger-stack-frame-as-list
+ "^ (edebug"
+ "^ edebug"))
(forward-line 1)
(delete-region last-ok-point (point))
)))
@@ -3816,7 +3898,7 @@ You must include newlines in FMT to break lines, but one newline is appended."
(setq truncate-lines t)
(setq buf-window (selected-window))
(goto-char (point-max))
- (insert (apply 'edebug-format fmt args) "\n")
+ (insert (apply #'edebug-format fmt args) "\n")
;; Make it visible.
(vertical-motion (- 1 (window-height)))
(set-window-start buf-window (point))
@@ -3831,7 +3913,7 @@ You must include newlines in FMT to break lines, but one newline is appended."
(defun edebug-trace (fmt &rest args)
"Convenience call to `edebug-trace-display' using `edebug-trace-buffer'."
- (apply 'edebug-trace-display edebug-trace-buffer fmt args))
+ (apply #'edebug-trace-display edebug-trace-buffer fmt args))
;;; Frequency count and coverage
diff --git a/lisp/emacs-lisp/eieio-base.el b/lisp/emacs-lisp/eieio-base.el
index 986d0285172..33c71ec5807 100644
--- a/lisp/emacs-lisp/eieio-base.el
+++ b/lisp/emacs-lisp/eieio-base.el
@@ -294,8 +294,7 @@ Second, any text properties will be stripped from strings."
(cond ((consp proposed-value)
;; Lists with something in them need special treatment.
(let* ((slot-idx (- (eieio--slot-name-index class slot)
- (eval-when-compile
- (length (cl-struct-slot-info 'eieio--object)))))
+ (eval-when-compile eieio--object-num-slots)))
(type (cl--slot-descriptor-type (aref (eieio--class-slots class)
slot-idx)))
(classtype (eieio-persistent-slot-type-is-class-p type)))
diff --git a/lisp/emacs-lisp/eieio-compat.el b/lisp/emacs-lisp/eieio-compat.el
index 83409ef100b..e6e6d118709 100644
--- a/lisp/emacs-lisp/eieio-compat.el
+++ b/lisp/emacs-lisp/eieio-compat.el
@@ -105,10 +105,10 @@ Summary:
(declare (doc-string 3) (obsolete cl-defmethod "25.1")
(debug
(&define ; this means we are defining something
- [&or name ("setf" :name setf name)]
+ [&or name ("setf" name :name setf)]
;; ^^ This is the methods symbol
[ &optional symbolp ] ; this is key :before etc
- list ; arguments
+ cl-generic-method-args ; arguments
[ &optional stringp ] ; documentation string
def-body ; part to be debugged
)))
@@ -145,7 +145,7 @@ Summary:
;; interleaved list comes before the class's non-interleaved list.
51 #'cl--generic-struct-tag
(lambda (tag &rest _)
- (and (symbolp tag) (boundp tag) (setq tag (symbol-value tag))
+ (and (symbolp tag) (setq tag (cl--find-class tag))
(eieio--class-p tag)
(let ((superclasses (eieio--class-precedence-list tag))
(specializers ()))
@@ -266,7 +266,7 @@ Summary:
;; Local Variables:
-;; generated-autoload-file: "eieio-core.el"
+;; generated-autoload-file: "eieio-loaddefs.el"
;; End:
(provide 'eieio-compat)
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index 111fbca3aa0..dfe1c06bfaf 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -32,7 +32,7 @@
;;; Code:
(require 'cl-lib)
-(require 'pcase)
+(require 'eieio-loaddefs nil t)
;;;
;; A few functions that are better in the official EIEIO src, but
@@ -107,21 +107,14 @@ Currently under control of this var:
(cl-declaim (optimize (safety 1))))
-(cl-defstruct (eieio--object
- (:type vector) ;We manage our own tagging system.
- (:constructor nil)
- (:copier nil))
- ;; `class-tag' holds a symbol, which is not the class name, but is instead
- ;; properly prefixed as an internal EIEIO thingy and which holds the class
- ;; object/struct in its `symbol-value' slot.
- class-tag)
+(eval-and-compile
+ (defconst eieio--object-num-slots 1))
-(eval-when-compile
- (defconst eieio--object-num-slots
- (length (cl-struct-slot-info 'eieio--object))))
+(defsubst eieio--object-class-tag (obj)
+ (aref obj 0))
(defsubst eieio--object-class (obj)
- (symbol-value (eieio--object-class-tag obj)))
+ (eieio--object-class-tag obj))
;;; Important macros used internally in eieio.
@@ -165,13 +158,8 @@ Return nil if that option doesn't exist."
(defun eieio-object-p (obj)
"Return non-nil if OBJ is an EIEIO object."
- (and (vectorp obj)
- (> (length obj) 0)
- (let ((tag (eieio--object-class-tag obj)))
- (and (symbolp tag)
- ;; (eq (symbol-function tag) :quick-object-witness-check)
- (boundp tag)
- (eieio--class-p (symbol-value tag))))))
+ (and (recordp obj)
+ (eieio--class-p (eieio--object-class-tag obj))))
(define-obsolete-function-alias 'object-p 'eieio-object-p "25.1")
@@ -495,18 +483,11 @@ See `defclass' for more information."
(if clearparent (setf (eieio--class-parents newc) nil))
;; Create the cached default object.
- (let ((cache (make-vector (+ (length (eieio--class-slots newc))
- (eval-when-compile eieio--object-num-slots))
- nil))
- ;; We don't strictly speaking need to use a symbol, but the old
- ;; code used the class's name rather than the class's object, so
- ;; we follow this preference for using a symbol, which is probably
- ;; convenient to keep the printed representation of such Elisp
- ;; objects readable.
- (tag (intern (format "eieio-class-tag--%s" cname))))
- (set tag newc)
- (fset tag :quick-object-witness-check)
- (setf (eieio--object-class-tag cache) tag)
+ (let ((cache (make-record newc
+ (+ (length (eieio--class-slots newc))
+ (eval-when-compile eieio--object-num-slots)
+ -1)
+ nil)))
(let ((eieio-skip-typecheck t))
;; All type-checking has been done to our satisfaction
;; before this call. Don't waste our time in this call..
@@ -756,9 +737,7 @@ Argument FN is the function calling this verifier."
;; The slot-missing method is a cool way of allowing an object author
;; to intercept missing slot definitions. Since it is also the LAST
;; thing called in this fn, its return value would be retrieved.
- (slot-missing obj slot 'oref)
- ;;(signal 'invalid-slot-name (list (eieio-object-name obj) slot))
- )
+ (slot-missing obj slot 'oref))
(cl-check-type obj eieio-object)
(eieio-barf-if-slot-unbound (aref obj c) obj slot 'oref))))
@@ -780,9 +759,7 @@ Fills in OBJ's SLOT with its default value."
;; Oref that slot.
(aref (eieio--class-class-allocation-values cl)
c)
- (slot-missing obj slot 'oref-default)
- ;;(signal 'invalid-slot-name (list (class-name cl) slot))
- )
+ (slot-missing obj slot 'oref-default))
(eieio-barf-if-slot-unbound
(let ((val (cl--slot-descriptor-initform
(aref (eieio--class-slots cl)
@@ -822,9 +799,7 @@ Fills in OBJ's SLOT with VALUE."
(aset (eieio--class-class-allocation-values class)
c value))
;; See oref for comment on `slot-missing'
- (slot-missing obj slot 'oset value)
- ;;(signal 'invalid-slot-name (list (eieio-object-name obj) slot))
- )
+ (slot-missing obj slot 'oset value))
(eieio--validate-slot-value class c value slot)
(aset obj c value))))
@@ -1065,11 +1040,13 @@ method invocation orders of the involved classes."
;; part of the dispatch code.
50 #'cl--generic-struct-tag
(lambda (tag &rest _)
- (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag))
- (mapcar #'eieio--class-name
- (eieio--class-precedence-list (symbol-value tag))))))
+ (let ((class (cl--find-class tag)))
+ (and (eieio--class-p class)
+ (mapcar #'eieio--class-name
+ (eieio--class-precedence-list class))))))
(cl-defmethod cl-generic-generalizers :extra "class" (specializer)
+ "Support for dispatch on types defined by EIEIO's `defclass'."
;; CLHS says:
;; A class must be defined before it can be used as a parameter
;; specializer in a defmethod form.
@@ -1098,100 +1075,10 @@ method invocation orders of the involved classes."
#'eieio--generic-subclass-specializers)
(cl-defmethod cl-generic-generalizers ((_specializer (head subclass)))
+ "Support for (subclass CLASS) specializers.
+These match if the argument is the name of a subclass of CLASS."
(list eieio--generic-subclass-generalizer))
-
-;;;### (autoloads nil "eieio-compat" "eieio-compat.el" "e91175056972ff549f07b83740ad04eb")
-;;; Generated autoloads from eieio-compat.el
-
-(autoload 'eieio--defalias "eieio-compat" "\
-Like `defalias', but with less side-effects.
-More specifically, it has no side-effects at all when the new function
-definition is the same (`eq') as the old one.
-
-\(fn NAME BODY)" nil nil)
-
-(autoload 'defgeneric "eieio-compat" "\
-Create a generic function METHOD.
-DOC-STRING is the base documentation for this class. A generic
-function has no body, as its purpose is to decide which method body
-is appropriate to use. Uses `defmethod' to create methods, and calls
-`defgeneric' for you. With this implementation the ARGS are
-currently ignored. You can use `defgeneric' to apply specialized
-top level documentation to a method.
-
-\(fn METHOD ARGS &optional DOC-STRING)" nil t)
-
-(function-put 'defgeneric 'doc-string-elt '3)
-
-(make-obsolete 'defgeneric 'cl-defgeneric '"25.1")
-
-(autoload 'defmethod "eieio-compat" "\
-Create a new METHOD through `defgeneric' with ARGS.
-
-The optional second argument KEY is a specifier that
-modifies how the method is called, including:
- :before - Method will be called before the :primary
- :primary - The default if not specified
- :after - Method will be called after the :primary
- :static - First arg could be an object or class
-The next argument is the ARGLIST. The ARGLIST specifies the arguments
-to the method as with `defun'. The first argument can have a type
-specifier, such as:
- ((VARNAME CLASS) ARG2 ...)
-where VARNAME is the name of the local variable for the method being
-created. The CLASS is a class symbol for a class made with `defclass'.
-A DOCSTRING comes after the ARGLIST, and is optional.
-All the rest of the args are the BODY of the method. A method will
-return the value of the last form in the BODY.
-
-Summary:
-
- (defmethod mymethod [:before | :primary | :after | :static]
- ((typearg class-name) arg2 &optional opt &rest rest)
- \"doc-string\"
- body)
-
-\(fn METHOD &rest ARGS)" nil t)
-
-(function-put 'defmethod 'doc-string-elt '3)
-
-(make-obsolete 'defmethod 'cl-defmethod '"25.1")
-
-(autoload 'eieio--defgeneric-init-form "eieio-compat" "\
-
-
-\(fn METHOD DOC-STRING)" nil nil)
-
-(autoload 'eieio--defmethod "eieio-compat" "\
-
-
-\(fn METHOD KIND ARGCLASS CODE)" nil nil)
-
-(autoload 'eieio-defmethod "eieio-compat" "\
-Obsolete work part of an old version of the `defmethod' macro.
-
-\(fn METHOD ARGS)" nil nil)
-
-(make-obsolete 'eieio-defmethod 'cl-defmethod '"24.1")
-
-(autoload 'eieio-defgeneric "eieio-compat" "\
-Obsolete work part of an old version of the `defgeneric' macro.
-
-\(fn METHOD DOC-STRING)" nil nil)
-
-(make-obsolete 'eieio-defgeneric 'cl-defgeneric '"24.1")
-
-(autoload 'eieio-defclass "eieio-compat" "\
-
-
-\(fn CNAME SUPERCLASSES SLOTS OPTIONS)" nil nil)
-
-(make-obsolete 'eieio-defclass 'eieio-defclass-internal '"25.1")
-
-;;;***
-
-
(provide 'eieio-core)
;;; eieio-core.el ends here
diff --git a/lisp/emacs-lisp/eieio-custom.el b/lisp/emacs-lisp/eieio-custom.el
index 38670253325..e82eaa2b01f 100644
--- a/lisp/emacs-lisp/eieio-custom.el
+++ b/lisp/emacs-lisp/eieio-custom.el
@@ -473,7 +473,7 @@ Return the symbol for the group, or nil"
(provide 'eieio-custom)
;; Local variables:
-;; generated-autoload-file: "eieio.el"
+;; generated-autoload-file: "eieio-loaddefs.el"
;; End:
;;; eieio-custom.el ends here
diff --git a/lisp/emacs-lisp/eieio-datadebug.el b/lisp/emacs-lisp/eieio-datadebug.el
index 624757f229a..8ef92df513e 100644
--- a/lisp/emacs-lisp/eieio-datadebug.el
+++ b/lisp/emacs-lisp/eieio-datadebug.el
@@ -59,7 +59,7 @@ PREFIX is the text that precedes the button.
PREBUTTONTEXT is some text between PREFIX and the object button."
(let* ((start (point))
(end nil)
- (str (object-print object))
+ (str (cl-prin1-to-string object))
(class (eieio-object-class object))
(tip (format "Object %s\nClass: %S\nParent(s): %S\n%d slots"
(eieio-object-name-string object)
diff --git a/lisp/emacs-lisp/eieio-opt.el b/lisp/emacs-lisp/eieio-opt.el
index d614c71a32b..ba4331f126b 100644
--- a/lisp/emacs-lisp/eieio-opt.el
+++ b/lisp/emacs-lisp/eieio-opt.el
@@ -349,7 +349,7 @@ INDENT is the current indentation level."
(provide 'eieio-opt)
;; Local variables:
-;; generated-autoload-file: "eieio.el"
+;; generated-autoload-file: "eieio-loaddefs.el"
;; End:
;;; eieio-opt.el ends here
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el
index 1a1ad88f975..1a7de55fcef 100644
--- a/lisp/emacs-lisp/eieio.el
+++ b/lisp/emacs-lisp/eieio.el
@@ -235,7 +235,7 @@ This method is obsolete."
(let ((f (intern (format "%s-child-p" name))))
`((defalias ',f ',testsym2)
(make-obsolete
- ',f ,(format "use (cl-typep ... '%s) instead" name)
+ ',f ,(format "use (cl-typep ... \\='%s) instead" name)
"25.1"))))
;; When using typep, (typep OBJ 'myclass) returns t for objects which
@@ -337,14 +337,12 @@ variable name of the same name as the slot."
;; hard-coded in random .elc files.
(defun eieio-pcase-slot-index-table (obj)
"Return some data structure from which can be extracted the slot offset."
- (eieio--class-index-table
- (symbol-value (eieio--object-class-tag obj))))
+ (eieio--class-index-table (eieio--object-class obj)))
(defun eieio-pcase-slot-index-from-index-table (index-table slot)
"Find the index to pass to `aref' to access SLOT."
(let ((index (gethash slot index-table)))
- (if index (+ (eval-when-compile
- (length (cl-struct-slot-info 'eieio--object)))
+ (if index (+ (eval-when-compile eieio--object-num-slots)
index))))
(pcase-defmacro eieio (&rest fields)
@@ -678,7 +676,8 @@ This class is not stored in the `parent' slot of a class vector."
(setq eieio-default-superclass (cl--find-class 'eieio-default-superclass))
-(defalias 'standard-class 'eieio-default-superclass)
+(define-obsolete-function-alias 'standard-class
+ 'eieio-default-superclass "26.1")
(cl-defgeneric make-instance (class &rest initargs)
"Make a new instance of CLASS based on INITARGS.
@@ -765,11 +764,7 @@ dynamically set from SLOTS."
;; Shared initialize will parse our slots for us.
(shared-initialize this slots))
-(cl-defgeneric slot-missing (object slot-name operation &optional new-value)
- "Method invoked when an attempt to access a slot in OBJECT fails.")
-
-(cl-defmethod slot-missing ((object eieio-default-superclass) slot-name
- _operation &optional _new-value)
+(cl-defgeneric slot-missing (object slot-name _operation &optional _new-value)
"Method invoked when an attempt to access a slot in OBJECT fails.
SLOT-NAME is the name of the failed slot, OPERATION is the type of access
that was requested, and optional NEW-VALUE is the value that was desired
@@ -777,8 +772,9 @@ to be set.
This method is called from `oref', `oset', and other functions which
directly reference slots in EIEIO objects."
- (signal 'invalid-slot-name (list (eieio-object-name object)
- slot-name)))
+ (signal 'invalid-slot-name
+ (list (if (eieio-object-p object) (eieio-object-name object) object)
+ slot-name)))
(cl-defgeneric slot-unbound (object class slot-name fn)
"Slot unbound is invoked during an attempt to reference an unbound slot.")
@@ -815,22 +811,20 @@ first and modify the returned object.")
(if params (shared-initialize nobj params))
nobj))
-(cl-defgeneric destructor (this &rest params)
- "Destructor for cleaning up any dynamic links to our object.")
-
-(cl-defmethod destructor ((_this eieio-default-superclass) &rest _params)
- "Destructor for cleaning up any dynamic links to our object.
-Argument THIS is the object being destroyed. PARAMS are additional
-ignored parameters."
+(cl-defgeneric destructor (_this &rest _params)
+ "Destructor for cleaning up any dynamic links to our object."
+ (declare (obsolete nil "26.1"))
;; No cleanup... yet.
- )
+ nil)
-(cl-defgeneric object-print (this &rest strings)
- "Pretty printer for object THIS. Call function `object-name' with STRINGS.
+(cl-defgeneric object-print (this &rest _strings)
+ "Pretty printer for object THIS.
It is sometimes useful to put a summary of the object into the
default #<notation> string when using EIEIO browsing tools.
-Implement this method to customize the summary.")
+Implement this method to customize the summary."
+ (declare (obsolete cl-print-object "26.1"))
+ (format "%S" this))
(cl-defmethod object-print ((this eieio-default-superclass) &rest strings)
"Pretty printer for object THIS. Call function `object-name' with STRINGS.
@@ -846,6 +840,12 @@ When passing in extra strings from child classes, always remember
to prepend a space."
(eieio-object-name this (apply #'concat strings)))
+
+(cl-defmethod cl-print-object ((object eieio-default-superclass) stream)
+ "Default printer for EIEIO objects."
+ ;; Fallback to the old `object-print'.
+ (princ (object-print object) stream))
+
(defvar eieio-print-depth 0
"When printing, keep track of the current indentation depth.")
@@ -938,73 +938,18 @@ this object."
;;; Unimplemented functions from CLOS
;;
-(defun change-class (_obj _class)
+(defun eieio-change-class (_obj _class)
"Change the class of OBJ to type CLASS.
This may create or delete slots, but does not affect the return value
of `eq'."
(error "EIEIO: `change-class' is unimplemented"))
+(define-obsolete-function-alias 'change-class 'eieio-change-class "26.1")
;; Hook ourselves into help system for describing classes and methods.
;; FIXME: This is not actually needed any more since we can click on the
;; hyperlink from the constructor's docstring to see the type definition.
(add-hook 'help-fns-describe-function-functions 'eieio-help-constructor)
-;;; Interfacing with edebug
-;;
-(defun eieio-edebug-prin1-to-string (print-function object &optional noescape)
- "Display EIEIO OBJECT in fancy format.
-
-Used as advice around `edebug-prin1-to-string', held in the
-variable PRINT-FUNCTION. Optional argument NOESCAPE is passed to
-`prin1-to-string' when appropriate."
- (cond ((eieio--class-p object) (eieio--class-print-name object))
- ((eieio-object-p object) (object-print object))
- ((and (listp object) (or (eieio--class-p (car object))
- (eieio-object-p (car object))))
- (concat "(" (mapconcat
- (lambda (x) (eieio-edebug-prin1-to-string print-function x))
- object " ")
- ")"))
- (t (funcall print-function object noescape))))
-
-(advice-add 'edebug-prin1-to-string
- :around #'eieio-edebug-prin1-to-string)
-
-
-;;; Start of automatically extracted autoloads.
-
-;;;### (autoloads nil "eieio-custom" "eieio-custom.el" "5a754881773067a24cfcfc6dcff91995")
-;;; Generated autoloads from eieio-custom.el
-
-(autoload 'customize-object "eieio-custom" "\
-Customize OBJ in a custom buffer.
-Optional argument GROUP is the sub-group of slots to display.
-
-\(fn OBJ &optional GROUP)" nil nil)
-
-;;;***
-
-;;;### (autoloads nil "eieio-opt" "eieio-opt.el" "04b22dbeb00abbaba0b1ae17b1935a36")
-;;; Generated autoloads from eieio-opt.el
-
-(autoload 'eieio-browse "eieio-opt" "\
-Create an object browser window to show all objects.
-If optional ROOT-CLASS, then start with that, otherwise start with
-variable `eieio-default-superclass'.
-
-\(fn &optional ROOT-CLASS)" t nil)
-
-(define-obsolete-function-alias 'eieio-help-class 'cl--describe-class "25.1")
-
-(autoload 'eieio-help-constructor "eieio-opt" "\
-Describe CTR if it is a class constructor.
-
-\(fn CTR)" nil nil)
-
-;;;***
-
-;;; End of automatically extracted autoloads.
-
(provide 'eieio)
;;; eieio ends here
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 87e90ac080d..a05bd7cc4d4 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -186,8 +186,9 @@ expression point is on."
:group 'eldoc :lighter eldoc-minor-mode-string
(setq eldoc-last-message nil)
(cond
- ((memq eldoc-documentation-function '(nil ignore))
- (message "There is no ElDoc support in this buffer")
+ ((not (eldoc--supported-p))
+ (when (called-interactively-p 'any)
+ (message "There is no ElDoc support in this buffer"))
(setq eldoc-mode nil))
(eldoc-mode
(when eldoc-print-after-edit
@@ -197,32 +198,26 @@ expression point is on."
(t
(kill-local-variable 'eldoc-message-commands)
(remove-hook 'post-command-hook 'eldoc-schedule-timer t)
- (remove-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area t))))
+ (remove-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area t)
+ (when eldoc-timer
+ (cancel-timer eldoc-timer)
+ (setq eldoc-timer nil)))))
;;;###autoload
-(define-minor-mode global-eldoc-mode
- "Toggle Global Eldoc mode on or off.
-With a prefix argument ARG, enable Global Eldoc mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil, and toggle it if ARG is ‘toggle’.
-
-If Global Eldoc mode is on, `eldoc-mode' will be enabled in all
-buffers where it's applicable. These are buffers that have modes
-that have enabled eldoc support. See `eldoc-documentation-function'."
+(define-globalized-minor-mode global-eldoc-mode eldoc-mode turn-on-eldoc-mode
:group 'eldoc
- :global t
:initialize 'custom-initialize-delay
- :init-value t
- (setq eldoc-last-message nil)
- (if global-eldoc-mode
- (progn
- (add-hook 'post-command-hook #'eldoc-schedule-timer)
- (add-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area))
- (remove-hook 'post-command-hook #'eldoc-schedule-timer)
- (remove-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area)))
+ :init-value t)
;;;###autoload
-(define-obsolete-function-alias 'turn-on-eldoc-mode 'eldoc-mode "24.4")
+(defun turn-on-eldoc-mode ()
+ "Turn on `eldoc-mode' if the buffer has eldoc support enabled.
+See `eldoc-documentation-function' for more detail."
+ (when (eldoc--supported-p)
+ (eldoc-mode 1)))
+
+(defun eldoc--supported-p ()
+ (not (memq eldoc-documentation-function '(nil ignore))))
(defun eldoc-schedule-timer ()
@@ -423,7 +418,7 @@ return any documentation.")
"down-list" "end-of-" "exchange-point-and-mark" "forward-" "goto-"
"handle-select-window" "indent-for-tab-command" "left-" "mark-page"
"mark-paragraph" "mouse-set-point" "move-" "move-beginning-of-"
- "move-end-of-" "next-" "other-window" "pop-global-mark" "previous-"
+ "move-end-of-" "newline" "next-" "other-window" "pop-global-mark" "previous-"
"recenter" "right-" "scroll-" "self-insert-command" "split-window-"
"up-list")
diff --git a/lisp/emacs-lisp/elint.el b/lisp/emacs-lisp/elint.el
index 64d96d9847e..cce9553ff6a 100644
--- a/lisp/emacs-lisp/elint.el
+++ b/lisp/emacs-lisp/elint.el
@@ -1,4 +1,4 @@
-;;; elint.el --- Lint Emacs Lisp
+;;; elint.el --- Lint Emacs Lisp -*- lexical-binding: t -*-
;; Copyright (C) 1997, 2001-2017 Free Software Foundation, Inc.
@@ -27,7 +27,7 @@
;; misspellings and undefined variables, although it can also catch
;; function calls with the wrong number of arguments.
-;; To use, call elint-current-buffer or elint-defun to lint a buffer
+;; To use, call `elint-current-buffer' or `elint-defun' to lint a buffer
;; or defun. The first call runs `elint-initialize' to set up some
;; argument data, which may take a while.
@@ -154,6 +154,9 @@ Set by `elint-initialize', if `elint-scan-preloaded' is non-nil.")
"Regexp matching elements of `preloaded-file-list' to ignore.
We ignore them because they contain no definitions of use to Elint.")
+(defvar elint-running)
+(defvar elint-current-pos) ; dynamically bound in elint-top-form
+
;;;
;;; ADT: top-form
;;;
@@ -372,7 +375,7 @@ Returns the forms."
(let ((elint-current-pos (point)))
;; non-list check could be here too. errors may be out of seq.
;; quoted check cannot be elsewhere, since quotes skipped.
- (if (looking-back "'" (1- (point)))
+ (if (= (preceding-char) ?\')
;; Eg cust-print.el uses ' as a comment syntax.
(elint-warning "Skipping quoted form `%c%.20s...'" ?\'
(read (current-buffer)))
@@ -862,7 +865,7 @@ CODE can be a lambda expression, a macro, or byte-compiled code."
(t (elint-error "Not a function object: %s" form)
env))))
-(defun elint-check-quote-form (form env)
+(defun elint-check-quote-form (_form env)
"Lint the quote FORM in ENV."
env)
@@ -903,8 +906,7 @@ CODE can be a lambda expression, a macro, or byte-compiled code."
"Check the when/unless/and/or FORM in ENV.
Does basic handling of `featurep' tests."
(let ((func (car form))
- (test (cadr form))
- sym)
+ (test (cadr form)))
;; Misses things like (and t (featurep 'xemacs))
;; Check byte-compile-maybe-guarded.
(cond ((and (memq func '(when and))
@@ -967,8 +969,6 @@ Does basic handling of `featurep' tests."
;;; Message functions
;;;
-(defvar elint-current-pos) ; dynamically bound in elint-top-form
-
(defun elint-log (type string args)
(elint-log-message (format "%s:%d:%s: %s"
(let ((f (buffer-file-name)))
@@ -1038,8 +1038,6 @@ Insert HEADER followed by a blank line if non-nil."
(display-buffer (elint-get-log-buffer))
(sit-for 0)))
-(defvar elint-running)
-
(defun elint-set-mode-line (&optional on)
"Set the mode-line-process of the Elint log buffer."
(with-current-buffer (elint-get-log-buffer)
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
index 7d99cb30274..4cf9d9609e9 100644
--- a/lisp/emacs-lisp/ert-x.el
+++ b/lisp/emacs-lisp/ert-x.el
@@ -97,7 +97,7 @@ To be used in ERT tests. If BODY finishes successfully, the test
buffer is killed; if there is an error, the test buffer is kept
around on error for further inspection. Its name is derived from
the name of the test and the result of NAME-FORM."
- (declare (debug ((form) body))
+ (declare (debug ((":name" form) body))
(indent 1))
`(ert--call-with-test-buffer ,name-form (lambda () ,@body)))
@@ -285,6 +285,30 @@ BUFFER defaults to current buffer. Does not modify BUFFER."
(kill-buffer clone)))))))
+(defmacro ert-with-message-capture (var &rest body)
+ "Execute BODY while collecting anything written with `message' in VAR.
+
+Capture all messages produced by `message' when it is called from
+Lisp, and concatenate them separated by newlines into one string.
+
+This is useful for separating the issuance of messages by the
+code under test from the behavior of the *Messages* buffer."
+ (declare (debug (symbolp body))
+ (indent 1))
+ (let ((g-advice (cl-gensym)))
+ `(let* ((,var "")
+ (,g-advice (lambda (func &rest args)
+ (if (or (null args) (equal (car args) ""))
+ (apply func args)
+ (let ((msg (apply #'format-message args)))
+ (setq ,var (concat ,var msg "\n"))
+ (funcall func "%s" msg))))))
+ (advice-add 'message :around ,g-advice)
+ (unwind-protect
+ (progn ,@body)
+ (advice-remove 'message ,g-advice)))))
+
+
(provide 'ert-x)
;;; ert-x.el ends here
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 60916f4bed5..2c49a634e35 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -276,11 +276,13 @@ DATA is displayed to the user and should state the reason for skipping."
(defun ert--expand-should-1 (whole form inner-expander)
"Helper function for the `should' macro and its variants."
(let ((form
- (macroexpand form (cond
- ((boundp 'macroexpand-all-environment)
- macroexpand-all-environment)
- ((boundp 'cl-macro-environment)
- cl-macro-environment)))))
+ (macroexpand form (append (bound-and-true-p
+ byte-compile-macro-environment)
+ (cond
+ ((boundp 'macroexpand-all-environment)
+ macroexpand-all-environment)
+ ((boundp 'cl-macro-environment)
+ cl-macro-environment))))))
(cond
((or (atom form) (ert--special-operator-p (car form)))
(let ((value (cl-gensym "value-")))
@@ -1235,7 +1237,7 @@ SELECTOR is the selector that was used to select TESTS."
(funcall listener 'test-ended stats test result))
(setf (ert--stats-current-test stats) nil))))
-(defun ert-run-tests (selector listener)
+(defun ert-run-tests (selector listener &optional interactively)
"Run the tests specified by SELECTOR, sending progress updates to LISTENER."
(let* ((tests (ert-select-tests selector t))
(stats (ert--make-stats tests selector)))
@@ -1246,10 +1248,14 @@ SELECTOR is the selector that was used to select TESTS."
(let ((ert--current-run-stats stats))
(force-mode-line-update)
(unwind-protect
- (progn
- (cl-loop for test in tests do
- (ert-run-or-rerun-test stats test listener))
- (setq abortedp nil))
+ (cl-loop for test in tests do
+ (ert-run-or-rerun-test stats test listener)
+ (when (and interactively
+ (ert-test-quit-p
+ (ert-test-most-recent-result test))
+ (y-or-n-p "Abort testing? "))
+ (cl-return))
+ finally (setq abortedp nil))
(setf (ert--stats-aborted-p stats) abortedp)
(setf (ert--stats-end-time stats) (current-time))
(funcall listener 'run-ended stats abortedp)))
@@ -1441,7 +1447,8 @@ Returns the stats object."
(ert-test-result-expected-p
test result))
(1+ (ert--stats-test-pos stats test))
- (ert-test-name test)))))))))
+ (ert-test-name test)))))))
+ nil))
;;;###autoload
(defun ert-run-tests-batch-and-exit (&optional selector)
@@ -1451,6 +1458,12 @@ The exit status will be 0 if all test results were as expected, 1
on unexpected results, or 2 if the tool detected an error outside
of the tests (e.g. invalid SELECTOR or bug in the code that runs
the tests)."
+ (or noninteractive
+ (user-error "This function is only for use in batch mode"))
+ ;; Better crash loudly than attempting to recover from undefined
+ ;; behavior.
+ (setq attempt-stack-overflow-recovery nil
+ attempt-orderly-shutdown-on-fatal-signal nil)
(unwind-protect
(let ((stats (ert-run-tests-batch selector)))
(kill-emacs (if (zerop (ert-stats-completed-unexpected stats)) 0 1)))
@@ -1468,9 +1481,13 @@ The logfiles should have the `ert-run-tests-batch' format. When finished,
this exits Emacs, with status as per `ert-run-tests-batch-and-exit'."
(or noninteractive
(user-error "This function is only for use in batch mode"))
+ ;; Better crash loudly than attempting to recover from undefined
+ ;; behavior.
+ (setq attempt-stack-overflow-recovery nil
+ attempt-orderly-shutdown-on-fatal-signal nil)
(let ((nlogs (length command-line-args-left))
(ntests 0) (nrun 0) (nexpected 0) (nunexpected 0) (nskipped 0)
- nnotrun logfile notests badtests unexpected)
+ nnotrun logfile notests badtests unexpected skipped)
(with-temp-buffer
(while (setq logfile (pop command-line-args-left))
(erase-buffer)
@@ -1490,9 +1507,10 @@ Ran \\([0-9]+\\) tests, \\([0-9]+\\) results as expected\
(push logfile unexpected)
(setq nunexpected (+ nunexpected
(string-to-number (match-string 4)))))
- (if (match-string 5)
- (setq nskipped (+ nskipped
- (string-to-number (match-string 5)))))))))
+ (when (match-string 5)
+ (push logfile skipped)
+ (setq nskipped (+ nskipped
+ (string-to-number (match-string 5)))))))))
(setq nnotrun (- ntests nrun))
(message "\nSUMMARY OF TEST RESULTS")
(message "-----------------------")
@@ -1516,6 +1534,26 @@ Ran \\([0-9]+\\) tests, \\([0-9]+\\) results as expected\
(when unexpected
(message "%d files contained unexpected results:" (length unexpected))
(mapc (lambda (l) (message " %s" l)) unexpected))
+ ;; More details on hydra, where the logs are harder to get to.
+ (when (and (getenv "NIX_STORE")
+ (not (zerop (+ nunexpected nskipped))))
+ (message "\nDETAILS")
+ (message "-------")
+ (with-temp-buffer
+ (dolist (x (list (list skipped "skipped" "SKIPPED")
+ (list unexpected "unexpected" "FAILED")))
+ (mapc (lambda (l)
+ (erase-buffer)
+ (insert-file-contents l)
+ (message "%s:" l)
+ (when (re-search-forward (format "^[ \t]*[0-9]+ %s results:"
+ (nth 1 x))
+ nil t)
+ (while (and (zerop (forward-line 1))
+ (looking-at (format "^[ \t]*%s" (nth 2 x))))
+ (message "%s" (buffer-substring (line-beginning-position)
+ (line-end-position))))))
+ (car x)))))
(kill-emacs (cond ((or notests badtests (not (zerop nnotrun))) 2)
(unexpected 1)
(t 0)))))
@@ -1575,7 +1613,7 @@ Signals an error if no test name was read."
(let ((sym (intern-soft input)))
(if (ert-test-boundp sym)
sym
- (error "Input does not name a test")))))
+ (user-error "Input does not name a test")))))
(defun ert-read-test-name-at-point (prompt)
"Read the name of a test and return it as a symbol.
@@ -1601,7 +1639,7 @@ Nothing more than an interactive interface to `ert-make-test-unbound'."
(interactive)
(when (called-interactively-p 'any)
(unless (y-or-n-p "Delete all tests? ")
- (error "Aborted")))
+ (user-error "Aborted")))
;; We can't use `ert-select-tests' here since that gives us only
;; test objects, and going from them back to the test name symbols
;; can fail if the `ert-test' defstruct has been redefined.
@@ -2011,9 +2049,8 @@ and how to display message."
test result)))
(ert--results-update-stats-display-maybe ewoc stats)
(ewoc-invalidate ewoc node))))))))
- (ert-run-tests
- selector
- listener)))
+ (ert-run-tests selector listener t)))
+
;;;###autoload
(defalias 'ert 'ert-run-tests-interactively)
@@ -2057,14 +2094,23 @@ and how to display message."
'("ERT Results"
["Re-run all tests" ert-results-rerun-all-tests]
"--"
- ["Re-run test" ert-results-rerun-test-at-point]
- ["Debug test" ert-results-rerun-test-at-point-debugging-errors]
- ["Show test definition" ert-results-find-test-at-point-other-window]
+ ;; FIXME? Why are there (at least) 3 different ways to decide if
+ ;; there is a test at point?
+ ["Re-run test" ert-results-rerun-test-at-point
+ :active (car (ert--results-test-at-point-allow-redefinition))]
+ ["Debug test" ert-results-rerun-test-at-point-debugging-errors
+ :active (car (ert--results-test-at-point-allow-redefinition))]
+ ["Show test definition" ert-results-find-test-at-point-other-window
+ :active (ert-test-at-point)]
"--"
- ["Show backtrace" ert-results-pop-to-backtrace-for-test-at-point]
- ["Show messages" ert-results-pop-to-messages-for-test-at-point]
- ["Show `should' forms" ert-results-pop-to-should-forms-for-test-at-point]
- ["Describe test" ert-results-describe-test-at-point]
+ ["Show backtrace" ert-results-pop-to-backtrace-for-test-at-point
+ :active (ert--results-test-at-point-no-redefinition)]
+ ["Show messages" ert-results-pop-to-messages-for-test-at-point
+ :active (ert--results-test-at-point-no-redefinition)]
+ ["Show `should' forms" ert-results-pop-to-should-forms-for-test-at-point
+ :active (ert--results-test-at-point-no-redefinition)]
+ ["Describe test" ert-results-describe-test-at-point
+ :active (ert--results-test-at-point-no-redefinition)]
"--"
["Delete test" ert-delete-test]
"--"
@@ -2106,7 +2152,7 @@ To be used in the ERT results buffer."
To be used in the ERT results buffer."
(or (ert--results-test-node-or-null-at-point)
- (error "No test at point")))
+ (user-error "No test at point")))
(defun ert-results-next-test ()
"Move point to the next test.
@@ -2156,7 +2202,7 @@ To be used in the ERT results buffer."
(interactive)
(let ((name (ert-test-at-point)))
(unless name
- (error "No test at point"))
+ (user-error "No test at point"))
(ert-find-test-other-window name)))
(defun ert--test-name-button-action (button)
@@ -2215,22 +2261,24 @@ To be used in the ERT results buffer."
(and (ert-test-boundp sym)
sym))))
-(defun ert--results-test-at-point-no-redefinition ()
+(defun ert--results-test-at-point-no-redefinition (&optional error)
"Return the test at point, or nil.
-
+If optional argument ERROR is non-nil, signal an error rather than return nil.
To be used in the ERT results buffer."
(cl-assert (eql major-mode 'ert-results-mode))
- (if (ert--results-test-node-or-null-at-point)
- (let* ((node (ert--results-test-node-at-point))
- (test (ert--ewoc-entry-test (ewoc-data node))))
- test)
- (let ((progress-bar-begin ert--results-progress-bar-button-begin))
- (when (and (<= progress-bar-begin (point))
- (< (point) (button-end (button-at progress-bar-begin))))
- (let* ((test-index (- (point) progress-bar-begin))
- (test (aref (ert--stats-tests ert--results-stats)
+ (or
+ (if (ert--results-test-node-or-null-at-point)
+ (let* ((node (ert--results-test-node-at-point))
+ (test (ert--ewoc-entry-test (ewoc-data node))))
+ test)
+ (let ((progress-bar-begin ert--results-progress-bar-button-begin))
+ (when (and (<= progress-bar-begin (point))
+ (< (point) (button-end (button-at progress-bar-begin))))
+ (let* ((test-index (- (point) progress-bar-begin))
+ (test (aref (ert--stats-tests ert--results-stats)
test-index)))
- test)))))
+ test))))
+ (if error (user-error "No test at point"))))
(defun ert--results-test-at-point-allow-redefinition ()
"Look up the test at point, and check whether it has been redefined.
@@ -2315,7 +2363,7 @@ To be used in the ERT results buffer."
(cl-destructuring-bind (test redefinition-state)
(ert--results-test-at-point-allow-redefinition)
(when (null test)
- (error "No test at point"))
+ (user-error "No test at point"))
(let* ((stats ert--results-stats)
(progress-message (format "Running %stest %S"
(cl-ecase redefinition-state
@@ -2355,7 +2403,7 @@ To be used in the ERT results buffer."
To be used in the ERT results buffer."
(interactive)
- (let* ((test (ert--results-test-at-point-no-redefinition))
+ (let* ((test (ert--results-test-at-point-no-redefinition t))
(stats ert--results-stats)
(pos (ert--stats-test-pos stats test))
(result (aref (ert--stats-test-results stats) pos)))
@@ -2384,7 +2432,7 @@ To be used in the ERT results buffer."
To be used in the ERT results buffer."
(interactive)
- (let* ((test (ert--results-test-at-point-no-redefinition))
+ (let* ((test (ert--results-test-at-point-no-redefinition t))
(stats ert--results-stats)
(pos (ert--stats-test-pos stats test))
(result (aref (ert--stats-test-results stats) pos)))
@@ -2405,7 +2453,7 @@ To be used in the ERT results buffer."
To be used in the ERT results buffer."
(interactive)
- (let* ((test (ert--results-test-at-point-no-redefinition))
+ (let* ((test (ert--results-test-at-point-no-redefinition t))
(stats ert--results-stats)
(pos (ert--stats-test-pos stats test))
(result (aref (ert--stats-test-results stats) pos)))
@@ -2460,7 +2508,7 @@ To be used in the ERT results buffer."
stats)
for end-time across (ert--stats-test-end-times stats)
collect (list test
- (float-time (subtract-time
+ (float-time (time-subtract
end-time start-time))))))
(setq data (sort data (lambda (a b)
(> (cl-second a) (cl-second b)))))
@@ -2488,7 +2536,7 @@ To be used in the ERT results buffer."
"Display the documentation for TEST-OR-TEST-NAME (a symbol or ert-test)."
(interactive (list (ert-read-test-name-at-point "Describe test")))
(when (< emacs-major-version 24)
- (error "Requires Emacs 24"))
+ (user-error "Requires Emacs 24 or later"))
(let (test-name
test-definition)
(cl-etypecase test-or-test-name
@@ -2532,7 +2580,7 @@ To be used in the ERT results buffer."
To be used in the ERT results buffer."
(interactive)
- (ert-describe-test (ert--results-test-at-point-no-redefinition)))
+ (ert-describe-test (ert--results-test-at-point-no-redefinition t)))
;;; Actions on load/unload.
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 82132f27ff6..9b98f05ae81 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -43,6 +43,8 @@
;;; Code:
+(eval-when-compile (require 'cl-lib))
+
;;; User variables:
(defgroup find-function nil
@@ -182,15 +184,15 @@ See the functions `find-function' and `find-variable'."
LIBRARY should be a string (the name of the library)."
;; If the library is byte-compiled, try to find a source library by
;; the same name.
- (if (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
- (setq library (replace-match "" t t library)))
+ (when (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
+ (setq library (replace-match "" t t library)))
(or
(locate-file library
- (or find-function-source-path load-path)
- (find-library-suffixes))
+ (or find-function-source-path load-path)
+ (find-library-suffixes))
(locate-file library
- (or find-function-source-path load-path)
- load-file-rep-suffixes)
+ (or find-function-source-path load-path)
+ load-file-rep-suffixes)
(when (file-name-absolute-p library)
(let ((rel (find-library--load-name library)))
(when rel
@@ -201,8 +203,22 @@ LIBRARY should be a string (the name of the library)."
(locate-file rel
(or find-function-source-path load-path)
load-file-rep-suffixes)))))
+ (find-library--from-load-history library)
(error "Can't find library %s" library)))
+(defun find-library--from-load-history (library)
+ ;; In `load-history', the file may be ".elc", ".el", ".el.gz", and
+ ;; LIBRARY may be "foo.el" or "foo".
+ (let ((load-re
+ (concat "\\(" (regexp-quote (file-name-sans-extension library)) "\\)"
+ (regexp-opt (get-load-suffixes)) "\\'")))
+ (cl-loop
+ for (file . _) in load-history thereis
+ (and (stringp file) (string-match load-re file)
+ (let ((dir (substring file 0 (match-beginning 1)))
+ (basename (match-string 1 file)))
+ (locate-file basename (list dir) (find-library-suffixes)))))))
+
(defvar find-function-C-source-directory
(let ((dir (expand-file-name "src" source-directory)))
(if (file-accessible-directory-p dir) dir))
@@ -257,37 +273,64 @@ TYPE should be nil to find a function, or `defvar' to find a variable."
;;;###autoload
(defun find-library (library)
"Find the Emacs Lisp source of LIBRARY.
-LIBRARY should be a string (the name of the library)."
- (interactive
- (let* ((dirs (or find-function-source-path load-path))
- (suffixes (find-library-suffixes))
- (table (apply-partially 'locate-file-completion-table
- dirs suffixes))
- (def (if (eq (function-called-at-point) 'require)
- ;; `function-called-at-point' may return 'require
- ;; with `point' anywhere on this line. So wrap the
- ;; `save-excursion' below in a `condition-case' to
- ;; avoid reporting a scan-error here.
- (condition-case nil
- (save-excursion
- (backward-up-list)
- (forward-char)
- (forward-sexp 2)
- (thing-at-point 'symbol))
- (error nil))
- (thing-at-point 'symbol))))
- (when (and def (not (test-completion def table)))
- (setq def nil))
- (list
- (completing-read (if def (format "Library name (default %s): " def)
- "Library name: ")
- table nil nil nil nil def))))
- (let ((buf (find-file-noselect (find-library-name library))))
- (condition-case nil
- (prog1
- (switch-to-buffer buf)
- (run-hooks 'find-function-after-hook))
- (error (pop-to-buffer buf)))))
+
+Interactively, prompt for LIBRARY using the one at or near point."
+ (interactive (list (read-library-name)))
+ (prog1
+ (switch-to-buffer (find-file-noselect (find-library-name library)))
+ (run-hooks 'find-function-after-hook)))
+
+(defun read-library-name ()
+ "Read and return a library name, defaulting to the one near point.
+
+A library name is the filename of an Emacs Lisp library located
+in a directory under `load-path' (or `find-function-source-path',
+if non-nil)."
+ (let* ((dirs (or find-function-source-path load-path))
+ (suffixes (find-library-suffixes))
+ (table (apply-partially 'locate-file-completion-table
+ dirs suffixes))
+ (def (if (eq (function-called-at-point) 'require)
+ ;; `function-called-at-point' may return 'require
+ ;; with `point' anywhere on this line. So wrap the
+ ;; `save-excursion' below in a `condition-case' to
+ ;; avoid reporting a scan-error here.
+ (condition-case nil
+ (save-excursion
+ (backward-up-list)
+ (forward-char)
+ (forward-sexp 2)
+ (thing-at-point 'symbol))
+ (error nil))
+ (thing-at-point 'symbol))))
+ (when (and def (not (test-completion def table)))
+ (setq def nil))
+ (completing-read (if def
+ (format "Library name (default %s): " def)
+ "Library name: ")
+ table nil nil nil nil def)))
+
+;;;###autoload
+(defun find-library-other-window (library)
+ "Find the Emacs Lisp source of LIBRARY in another window.
+
+See `find-library' for more details."
+ (interactive (list (read-library-name)))
+ (prog1
+ (switch-to-buffer-other-window (find-file-noselect
+ (find-library-name library)))
+ (run-hooks 'find-function-after-hook)))
+
+;;;###autoload
+(defun find-library-other-frame (library)
+ "Find the Emacs Lisp source of LIBRARY in another frame.
+
+See `find-library' for more details."
+ (interactive (list (read-library-name)))
+ (prog1
+ (switch-to-buffer-other-frame (find-file-noselect
+ (find-library-name library)))
+ (run-hooks 'find-function-after-hook)))
;;;###autoload
(defun find-function-search-for-symbol (symbol type library)
diff --git a/lisp/emacs-lisp/generator.el b/lisp/emacs-lisp/generator.el
index 2ab01404bad..c96b400809b 100644
--- a/lisp/emacs-lisp/generator.el
+++ b/lisp/emacs-lisp/generator.el
@@ -77,7 +77,6 @@
;;; Code:
(require 'cl-lib)
-(require 'pcase)
(defvar cps--bindings nil)
(defvar cps--states nil)
diff --git a/lisp/emacs-lisp/let-alist.el b/lisp/emacs-lisp/let-alist.el
index 17bf7fb37fc..cf82fe3ec63 100644
--- a/lisp/emacs-lisp/let-alist.el
+++ b/lisp/emacs-lisp/let-alist.el
@@ -2,13 +2,16 @@
;; Copyright (C) 2014-2017 Free Software Foundation, Inc.
-;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
-;; Maintainer: Artur Malabarba <bruce.connor.am@gmail.com>
-;; Version: 1.0.4
+;; Author: Artur Malabarba <emacs@endlessparentheses.com>
+;; Package-Requires: ((emacs "24.1"))
+;; Version: 1.0.5
;; Keywords: extensions lisp
;; Prefix: let-alist
;; Separator: -
+;; This is an Elpa :core package. Don't use functionality that is not
+;; compatible with Emacs 24.1.
+
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
@@ -73,6 +76,11 @@ symbol, and each cdr is the same symbol without the `.'."
;; with other results in the clause below.
(list (cons data (intern (replace-match "" nil nil name)))))))
((not (consp data)) nil)
+ ((eq (car data) 'let-alist)
+ ;; For nested ‘let-alist’ forms, ignore symbols appearing in the
+ ;; inner body because they don’t refer to the alist currently
+ ;; being processed. See Bug#24641.
+ (let-alist--deep-dot-search (cadr data)))
(t (append (let-alist--deep-dot-search (car data))
(let-alist--deep-dot-search (cdr data))))))
@@ -134,7 +142,7 @@ displayed in the example above."
(let ((var (make-symbol "alist")))
`(let ((,var ,alist))
(let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var)))
- (delete-dups (let-alist--deep-dot-search body)))
+ (delete-dups (let-alist--deep-dot-search body)))
,@body))))
(provide 'let-alist)
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 24e9dc63ec3..1e38d44e1b1 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -164,10 +164,15 @@
(put 'defalias 'doc-string-elt 3)
(put 'defvaralias 'doc-string-elt 3)
(put 'define-category 'doc-string-elt 2)
+;; CL
+(put 'defconstant 'doc-string-elt 3)
+(put 'defparameter 'doc-string-elt 3)
(defvar lisp-doc-string-elt-property 'doc-string-elt
"The symbol property that holds the docstring position info.")
+(defconst lisp-prettify-symbols-alist '(("lambda" . ?λ))
+ "Alist of symbol/\"pretty\" characters to be displayed.")
;;;; Font-lock support.
@@ -257,6 +262,24 @@ This will generate compile-time constants from BINDINGS."
(funcall loop bindings)))))))
(funcall loop bindings)))
+(defun elisp--font-lock-backslash ()
+ (let* ((beg0 (match-beginning 0))
+ (end0 (match-end 0))
+ (ppss (save-excursion (syntax-ppss beg0))))
+ (and (nth 3 ppss) ;Inside a string.
+ (not (nth 5 ppss)) ;The \ is not itself \-escaped.
+ ;; Don't highlight the \( introduced because of
+ ;; `open-paren-in-column-0-is-defun-start'.
+ (not (eq ?\n (char-before beg0)))
+ (equal (ignore-errors
+ (car (read-from-string
+ (format "\"%s\""
+ (buffer-substring-no-properties
+ beg0 end0)))))
+ (buffer-substring-no-properties (1+ beg0) end0))
+ `(face ,font-lock-warning-face
+ help-echo "This \\ has no effect"))))
+
(let-when-compile
((lisp-fdefs '("defmacro" "defun"))
(lisp-vdefs '("defvar"))
@@ -409,6 +432,9 @@ This will generate compile-time constants from BINDINGS."
;; Words inside \\[] tend to be for `substitute-command-keys'.
(,(concat "\\\\\\\\\\[\\(" lisp-mode-symbol-regexp "\\)\\]")
(1 font-lock-constant-face prepend))
+ ;; Ineffective backslashes (typically in need of doubling).
+ ("\\(\\\\\\)\\([^\"\\]\\)"
+ (1 (elisp--font-lock-backslash) prepend))
;; Words inside ‘’ and `' tend to be symbol names.
(,(concat "[`‘]\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)"
lisp-mode-symbol-regexp "\\)['’]")
@@ -550,6 +576,13 @@ Lisp font lock syntactic face function."
font-lock-string-face))))
font-lock-comment-face))
+(defun lisp-adaptive-fill ()
+ "Return fill prefix found at point.
+Value for `adaptive-fill-function'."
+ ;; Adaptive fill mode gets the fill wrong for a one-line paragraph made of
+ ;; a single docstring. Let's fix it here.
+ (if (looking-at "\\s-+\"[^\n\"]+\"\\s-*$") ""))
+
(defun lisp-mode-variables (&optional lisp-syntax keywords-case-insensitive
elisp)
"Common initialization routine for lisp modes.
@@ -561,16 +594,14 @@ font-lock keywords will not be case sensitive."
(set-syntax-table lisp-mode-syntax-table))
(setq-local paragraph-ignore-fill-prefix t)
(setq-local fill-paragraph-function 'lisp-fill-paragraph)
- ;; Adaptive fill mode gets the fill wrong for a one-line paragraph made of
- ;; a single docstring. Let's fix it here.
- (setq-local adaptive-fill-function
- (lambda () (if (looking-at "\\s-+\"[^\n\"]+\"\\s-*$") "")))
+ (setq-local adaptive-fill-function #'lisp-adaptive-fill)
;; Adaptive fill mode gets in the way of auto-fill,
;; and should make no difference for explicit fill
;; because lisp-fill-paragraph should do the job.
;; I believe that newcomment's auto-fill code properly deals with it -stef
;;(set (make-local-variable 'adaptive-fill-mode) nil)
(setq-local indent-line-function 'lisp-indent-line)
+ (setq-local indent-region-function 'lisp-indent-region)
(setq-local outline-regexp ";;;\\(;* [^ \t\n]\\|###autoload\\)\\|(")
(setq-local outline-level 'lisp-outline-level)
(setq-local add-log-current-defun-function #'lisp-current-defun-name)
@@ -594,7 +625,7 @@ font-lock keywords will not be case sensitive."
(font-lock-extra-managed-props help-echo)
(font-lock-syntactic-face-function
. lisp-font-lock-syntactic-face-function)))
- (setq-local prettify-symbols-alist lisp--prettify-symbols-alist)
+ (setq-local prettify-symbols-alist lisp-prettify-symbols-alist)
(setq-local electric-pair-skip-whitespace 'chomp)
(setq-local electric-pair-open-newline-between-pairs nil))
@@ -655,9 +686,6 @@ font-lock keywords will not be case sensitive."
:type 'hook
:group 'lisp)
-(defconst lisp--prettify-symbols-alist
- '(("lambda" . ?λ)))
-
;;; Generic Lisp mode.
(defvar lisp-mode-map
@@ -705,11 +733,7 @@ or to switch back to an existing one."
;; Used in old LispM code.
(defalias 'common-lisp-mode 'lisp-mode)
-;; This will do unless inf-lisp.el is loaded.
-(defun lisp-eval-defun (&optional _and-go)
- "Send the current defun to the Lisp process made by \\[run-lisp]."
- (interactive)
- (error "Process lisp does not exist"))
+(autoload 'lisp-eval-defun "inf-lisp" nil t)
;; May still be used by some external Lisp-mode variant.
(define-obsolete-function-alias 'lisp-comment-indent
@@ -732,14 +756,110 @@ function is `common-lisp-indent-function'."
:type 'function
:group 'lisp)
-(defun lisp-indent-line (&optional _whole-exp)
- "Indent current line as Lisp code.
-With argument, indent any additional lines of the same expression
-rigidly along with this one."
- (interactive "P")
- (let ((indent (calculate-lisp-indent)) shift-amt
- (pos (- (point-max) (point)))
- (beg (progn (beginning-of-line) (point))))
+(defun lisp-ppss (&optional pos)
+ "Return Parse-Partial-Sexp State at POS, defaulting to point.
+Like `syntax-ppss' but includes the character address of the last
+complete sexp in the innermost containing list at position
+2 (counting from 0). This is important for lisp indentation."
+ (unless pos (setq pos (point)))
+ (let ((pss (syntax-ppss pos)))
+ (if (nth 9 pss)
+ (let ((sexp-start (car (last (nth 9 pss)))))
+ (parse-partial-sexp sexp-start pos nil nil (syntax-ppss sexp-start)))
+ pss)))
+
+(cl-defstruct (lisp-indent-state
+ (:constructor nil)
+ (:constructor lisp-indent-initial-state
+ (&aux (ppss (lisp-ppss))
+ (ppss-point (point))
+ (depth (car ppss))
+ (stack (make-list (1+ depth) nil)))))
+ stack ;; Cached indentation, per depth.
+ ppss
+ depth
+ ppss-point)
+
+(defun lisp-indent-calc-next (state)
+ "Move to next line and return calculated indent for it.
+STATE is updated by side effect, the first state should be
+created by `lisp-indent-initial-state'. This function may move
+by more than one line to cross a string literal."
+ (pcase-let (((cl-struct lisp-indent-state
+ (stack indent-stack) ppss depth ppss-point)
+ state))
+ ;; Parse this line so we can learn the state to indent the
+ ;; next line.
+ (while (let ((last-sexp (nth 2 ppss)))
+ (setq ppss (parse-partial-sexp
+ ppss-point (progn (end-of-line) (point))
+ nil nil ppss))
+ ;; Preserve last sexp of state (position 2) for
+ ;; `calculate-lisp-indent', if we're at the same depth.
+ (if (and (not (nth 2 ppss)) (= depth (car ppss)))
+ (setf (nth 2 ppss) last-sexp)
+ (setq last-sexp (nth 2 ppss)))
+ ;; Skip over newlines within strings.
+ (nth 3 ppss))
+ (let ((string-start (nth 8 ppss)))
+ (setq ppss (parse-partial-sexp (point) (point-max)
+ nil nil ppss 'syntax-table))
+ (setf (nth 2 ppss) string-start)) ; Finished a complete string.
+ (setq ppss-point (point)))
+ (setq ppss-point (point))
+ (let* ((next-depth (car ppss))
+ (depth-delta (- next-depth depth)))
+ (cond ((< depth-delta 0)
+ (setq indent-stack (nthcdr (- depth-delta) indent-stack)))
+ ((> depth-delta 0)
+ (setq indent-stack (nconc (make-list depth-delta nil)
+ indent-stack))))
+ (setq depth next-depth))
+ (prog1
+ (let (indent)
+ (cond ((= (forward-line 1) 1) nil)
+ ((car indent-stack))
+ ((integerp (setq indent (calculate-lisp-indent ppss)))
+ (setf (car indent-stack) indent))
+ ((consp indent) ; (COLUMN CONTAINING-SEXP-START)
+ (car indent))
+ ;; This only happens if we're in a string.
+ (t (error "This shouldn't happen"))))
+ (setf (lisp-indent-state-stack state) indent-stack)
+ (setf (lisp-indent-state-depth state) depth)
+ (setf (lisp-indent-state-ppss-point state) ppss-point)
+ (setf (lisp-indent-state-ppss state) ppss))))
+
+(defun lisp-indent-region (start end)
+ "Indent region as Lisp code, efficiently."
+ (save-excursion
+ (setq end (copy-marker end))
+ (goto-char start)
+ (beginning-of-line)
+ ;; The default `indent-region-line-by-line' doesn't hold a running
+ ;; parse state, which forces each indent call to reparse from the
+ ;; beginning. That has O(n^2) complexity.
+ (let* ((parse-state (lisp-indent-initial-state))
+ (pr (unless (minibufferp)
+ (make-progress-reporter "Indenting region..." (point) end))))
+ (let ((ppss (lisp-indent-state-ppss parse-state)))
+ (unless (or (and (bolp) (eolp)) (nth 3 ppss))
+ (lisp-indent-line (calculate-lisp-indent ppss))))
+ (let ((indent nil))
+ (while (progn (setq indent (lisp-indent-calc-next parse-state))
+ (< (point) end))
+ (unless (or (and (bolp) (eolp)) (not indent))
+ (lisp-indent-line indent))
+ (and pr (progress-reporter-update pr (point)))))
+ (and pr (progress-reporter-done pr))
+ (move-marker end nil))))
+
+(defun lisp-indent-line (&optional indent)
+ "Indent current line as Lisp code."
+ (interactive)
+ (let ((pos (- (point-max) (point)))
+ (indent (progn (beginning-of-line)
+ (or indent (calculate-lisp-indent (lisp-ppss))))))
(skip-chars-forward " \t")
(if (or (null indent) (looking-at "\\s<\\s<\\s<"))
;; Don't alter indentation of a ;;; comment line
@@ -751,11 +871,7 @@ rigidly along with this one."
;; as comment lines, not as code.
(progn (indent-for-comment) (forward-char -1))
(if (listp indent) (setq indent (car indent)))
- (setq shift-amt (- indent (current-column)))
- (if (zerop shift-amt)
- nil
- (delete-region beg (point))
- (indent-to indent)))
+ (indent-line-to indent))
;; If initial point was within line's indentation,
;; position after the indentation. Else stay at same point in text.
(if (> (- (point-max) pos) (point))
@@ -769,6 +885,10 @@ In usual case returns an integer: the column to indent to.
If the value is nil, that means don't change the indentation
because the line starts inside a string.
+PARSE-START may be a buffer position to start parsing from, or a
+parse state as returned by calling `parse-partial-sexp' up to the
+beginning of the current line.
+
The value can also be a list of the form (COLUMN CONTAINING-SEXP-START).
This means that following lines at the same level of indentation
should not necessarily be indented the same as this line.
@@ -782,12 +902,14 @@ is the buffer position of the start of the containing expression."
(desired-indent nil)
(retry t)
calculate-lisp-indent-last-sexp containing-sexp)
- (if parse-start
- (goto-char parse-start)
- (beginning-of-defun))
- ;; Find outermost containing sexp
- (while (< (point) indent-point)
- (setq state (parse-partial-sexp (point) indent-point 0)))
+ (cond ((or (markerp parse-start) (integerp parse-start))
+ (goto-char parse-start))
+ ((null parse-start) (beginning-of-defun))
+ (t (setq state parse-start)))
+ (unless state
+ ;; Find outermost containing sexp
+ (while (< (point) indent-point)
+ (setq state (parse-partial-sexp (point) indent-point 0))))
;; Find innermost containing sexp
(while (and retry
state
@@ -1057,103 +1179,34 @@ Lisp function does not specify a special indentation."
If optional arg ENDPOS is given, indent each line, stopping when
ENDPOS is encountered."
(interactive)
- (let ((indent-stack (list nil))
- (next-depth 0)
- ;; If ENDPOS is non-nil, use nil as STARTING-POINT
- ;; so that calculate-lisp-indent will find the beginning of
- ;; the defun we are in.
- ;; If ENDPOS is nil, it is safe not to scan before point
- ;; since every line we indent is more deeply nested than point is.
- (starting-point (if endpos nil (point)))
- (last-point (point))
- last-depth bol outer-loop-done inner-loop-done state this-indent)
- (or endpos
- ;; Get error now if we don't have a complete sexp after point.
- (save-excursion (forward-sexp 1)))
+ (let* ((parse-state (lisp-indent-initial-state)))
+ ;; We need a marker because we modify the buffer
+ ;; text preceding endpos.
+ (setq endpos (copy-marker
+ (if endpos endpos
+ ;; Get error now if we don't have a complete sexp
+ ;; after point.
+ (save-excursion (forward-sexp 1) (point)))))
(save-excursion
- (setq outer-loop-done nil)
- (while (if endpos (< (point) endpos)
- (not outer-loop-done))
- (setq last-depth next-depth
- inner-loop-done nil)
- ;; Parse this line so we can learn the state
- ;; to indent the next line.
- ;; This inner loop goes through only once
- ;; unless a line ends inside a string.
- (while (and (not inner-loop-done)
- (not (setq outer-loop-done (eobp))))
- (setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
- nil nil state))
- (setq next-depth (car state))
- ;; If the line contains a comment other than the sort
- ;; that is indented like code,
- ;; indent it now with indent-for-comment.
- ;; Comments indented like code are right already.
- ;; In any case clear the in-comment flag in the state
- ;; because parse-partial-sexp never sees the newlines.
- (if (car (nthcdr 4 state))
- (progn (indent-for-comment)
- (end-of-line)
- (setcar (nthcdr 4 state) nil)))
- ;; If this line ends inside a string,
- ;; go straight to next line, remaining within the inner loop,
- ;; and turn off the \-flag.
- (if (car (nthcdr 3 state))
- (progn
- (forward-line 1)
- (setcar (nthcdr 5 state) nil))
- (setq inner-loop-done t)))
- (and endpos
- (<= next-depth 0)
- (progn
- (setq indent-stack (nconc indent-stack
- (make-list (- next-depth) nil))
- last-depth (- last-depth next-depth)
- next-depth 0)))
- (forward-line 1)
- ;; Decide whether to exit.
- (if endpos
- ;; If we have already reached the specified end,
- ;; give up and do not reindent this line.
- (if (<= endpos (point))
- (setq outer-loop-done t))
- ;; If no specified end, we are done if we have finished one sexp.
- (if (<= next-depth 0)
- (setq outer-loop-done t)))
- (unless outer-loop-done
- (while (> last-depth next-depth)
- (setq indent-stack (cdr indent-stack)
- last-depth (1- last-depth)))
- (while (< last-depth next-depth)
- (setq indent-stack (cons nil indent-stack)
- last-depth (1+ last-depth)))
- ;; Now indent the next line according
- ;; to what we learned from parsing the previous one.
- (setq bol (point))
- (skip-chars-forward " \t")
- ;; But not if the line is blank, or just a comment
- ;; (except for double-semi comments; indent them as usual).
- (if (or (eobp) (looking-at "\\s<\\|\n"))
- nil
- (if (and (car indent-stack)
- (>= (car indent-stack) 0))
- (setq this-indent (car indent-stack))
- (let ((val (calculate-lisp-indent
- (if (car indent-stack) (- (car indent-stack))
- starting-point))))
- (if (null val)
- (setq this-indent val)
- (if (integerp val)
- (setcar indent-stack
- (setq this-indent val))
- (setcar indent-stack (- (car (cdr val))))
- (setq this-indent (car val))))))
- (if (and this-indent (/= (current-column) this-indent))
- (progn (delete-region bol (point))
- (indent-to this-indent)))))
- (or outer-loop-done
- (setq outer-loop-done (= (point) last-point))
- (setq last-point (point)))))))
+ (while (let ((indent (lisp-indent-calc-next parse-state))
+ (ppss (lisp-indent-state-ppss parse-state)))
+ ;; If the line contains a comment indent it now with
+ ;; `indent-for-comment'.
+ (when (and (nth 4 ppss) (<= (nth 8 ppss) endpos))
+ (save-excursion
+ (goto-char (lisp-indent-state-ppss-point parse-state))
+ (indent-for-comment)
+ (setf (lisp-indent-state-ppss-point parse-state)
+ (line-end-position))))
+ (when (< (point) endpos)
+ ;; Indent the next line, unless it's blank, or just a
+ ;; comment (we will `indent-for-comment' the latter).
+ (skip-chars-forward " \t")
+ (unless (or (eolp) (not indent)
+ (eq (char-syntax (char-after)) ?<))
+ (indent-line-to indent))
+ t))))
+ (move-marker endpos nil)))
(defun indent-pp-sexp (&optional arg)
"Indent each line of the list starting just after point, or prettyprint it.
@@ -1217,8 +1270,15 @@ and initial semicolons."
;;
;; The `fill-column' is temporarily bound to
;; `emacs-lisp-docstring-fill-column' if that value is an integer.
- (let ((paragraph-start (concat paragraph-start
- "\\|\\s-*\\([(;:\"]\\|`(\\|#'(\\)"))
+ (let ((paragraph-start
+ (concat paragraph-start
+ (format "\\|\\s-*\\([(;%s\"]\\|`(\\|#'(\\)"
+ ;; If we're inside a string (like the doc
+ ;; string), don't consider a colon to be
+ ;; a paragraph-start character.
+ (if (nth 3 (syntax-ppss))
+ ""
+ ":"))))
(paragraph-separate
(concat paragraph-separate "\\|\\s-*\".*[,\\.]$"))
(fill-column (if (and (integerp emacs-lisp-docstring-fill-column)
diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el
index 0172e3af261..0c1fe42fedb 100644
--- a/lisp/emacs-lisp/lisp.el
+++ b/lisp/emacs-lisp/lisp.el
@@ -398,6 +398,42 @@ is called as a function to find the defun's beginning."
(goto-char (if arg-+ve floor ceiling))
nil))))))))
+(defun beginning-of-defun--in-emptyish-line-p ()
+ "Return non-nil if the point is in an \"emptyish\" line.
+This means a line that consists entirely of comments and/or
+whitespace."
+;; See http://lists.gnu.org/archive/html/help-gnu-emacs/2016-08/msg00141.html
+ (save-excursion
+ (forward-line 0)
+ (< (line-end-position)
+ (let ((ppss (syntax-ppss)))
+ (when (nth 4 ppss)
+ (goto-char (nth 8 ppss)))
+ (forward-comment (point-max))
+ (point)))))
+
+(defun beginning-of-defun-comments (&optional arg)
+ "Move to the beginning of ARGth defun, including comments."
+ (interactive "^p")
+ (unless arg (setq arg 1))
+ (beginning-of-defun arg)
+ (let (first-line-p)
+ (while (let ((ppss (progn (setq first-line-p (= (forward-line -1) -1))
+ (syntax-ppss (line-end-position)))))
+ (while (and (nth 4 ppss) ; If eol is in a line-spanning comment,
+ (< (nth 8 ppss) (line-beginning-position)))
+ (goto-char (nth 8 ppss)) ; skip to comment start.
+ (setq ppss (syntax-ppss (line-end-position))))
+ (and (not first-line-p)
+ (progn (skip-syntax-backward
+ "-" (line-beginning-position))
+ (not (bolp))) ; Check for blank line.
+ (progn (parse-partial-sexp
+ (line-beginning-position) (line-end-position)
+ nil t (syntax-ppss (line-beginning-position)))
+ (eolp))))) ; Check for non-comment text.
+ (forward-line (if first-line-p 0 1))))
+
(defvar end-of-defun-function
(lambda () (forward-sexp 1))
"Function for `end-of-defun' to call.
@@ -478,48 +514,72 @@ is called as a function to find the defun's end."
(funcall end-of-defun-function)
(funcall skip)))))
-(defun mark-defun (&optional allow-extend)
+(defun mark-defun (&optional arg)
"Put mark at end of this defun, point at beginning.
The defun marked is the one that contains point or follows point.
+With positive ARG, mark this and that many next defuns; with negative
+ARG, change the direction of marking.
-Interactively, if this command is repeated
-or (in Transient Mark mode) if the mark is active,
-it marks the next defun after the ones already marked."
+If the mark is active, it marks the next or previous defun(s) after
+the one(s) already marked."
(interactive "p")
- (cond ((and allow-extend
- (or (and (eq last-command this-command) (mark t))
- (and transient-mark-mode mark-active)))
- (set-mark
- (save-excursion
- (goto-char (mark))
- (end-of-defun)
- (point))))
- (t
- (let ((opoint (point))
- beg end)
- (push-mark opoint)
- ;; Try first in this order for the sake of languages with nested
- ;; functions where several can end at the same place as with
- ;; the offside rule, e.g. Python.
- (beginning-of-defun)
- (setq beg (point))
- (end-of-defun)
- (setq end (point))
- (while (looking-at "^\n")
- (forward-line 1))
- (if (> (point) opoint)
- (progn
- ;; We got the right defun.
- (push-mark beg nil t)
- (goto-char end)
- (exchange-point-and-mark))
- ;; beginning-of-defun moved back one defun
- ;; so we got the wrong one.
- (goto-char opoint)
- (end-of-defun)
- (push-mark (point) nil t)
- (beginning-of-defun))
- (re-search-backward "^\n" (- (point) 1) t)))))
+ (setq arg (or arg 1))
+ ;; There is no `mark-defun-back' function - see
+ ;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-11/msg00079.html
+ ;; for explanation
+ (when (eq last-command 'mark-defun-back)
+ (setq arg (- arg)))
+ (when (< arg 0)
+ (setq this-command 'mark-defun-back))
+ (cond ((use-region-p)
+ (if (>= arg 0)
+ (set-mark
+ (save-excursion
+ (goto-char (mark))
+ ;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
+ (dotimes (_ignore arg)
+ (end-of-defun))
+ (point)))
+ (beginning-of-defun-comments (- arg))))
+ (t
+ (let ((opoint (point))
+ beg end)
+ (push-mark opoint)
+ ;; Try first in this order for the sake of languages with nested
+ ;; functions where several can end at the same place as with the
+ ;; offside rule, e.g. Python.
+ (beginning-of-defun-comments)
+ (setq beg (point))
+ (end-of-defun)
+ (setq end (point))
+ (when (or (and (<= (point) opoint)
+ (> arg 0))
+ (= beg (point-min))) ; we were before the first defun!
+ ;; beginning-of-defun moved back one defun so we got the wrong
+ ;; one. If ARG < 0, however, we actually want to go back.
+ (goto-char opoint)
+ (end-of-defun)
+ (setq end (point))
+ (beginning-of-defun-comments)
+ (setq beg (point)))
+ (goto-char beg)
+ (cond ((> arg 0)
+ ;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
+ (dotimes (_ignore arg)
+ (end-of-defun))
+ (setq end (point))
+ (push-mark end nil t)
+ (goto-char beg))
+ (t
+ (goto-char beg)
+ (unless (= arg -1) ; beginning-of-defun behaves
+ ; strange with zero arg - see
+ ; https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-02/msg00196.html
+ (beginning-of-defun (1- (- arg))))
+ (push-mark end nil t))))))
+ (skip-chars-backward "[:space:]\n")
+ (unless (bobp)
+ (forward-line 1)))
(defvar narrow-to-defun-include-comments nil
"If non-nil, `narrow-to-defun' will also show comments preceding the defun.")
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index 98bfff713a0..9bc194c478c 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -103,7 +103,7 @@ each clause."
(defun macroexp--funcall-if-compiled (_form)
"Pseudo function used internally by macroexp to delay warnings.
The purpose is to delay warnings to bytecomp.el, so they can use things
-like `byte-compile-log-warning' to get better file-and-line-number data
+like `byte-compile-warn' to get better file-and-line-number data
and also to avoid outputting the warning during normal execution."
nil)
(put 'macroexp--funcall-if-compiled 'byte-compile
@@ -122,7 +122,7 @@ and also to avoid outputting the warning during normal execution."
(defvar macroexp--warned (make-hash-table :test #'equal :weakness 'key))
(defun macroexp--warn-and-return (msg form &optional compile-only)
- (let ((when-compiled (lambda () (byte-compile-log-warning msg t))))
+ (let ((when-compiled (lambda () (byte-compile-warn "%s" msg))))
(cond
((null msg) form)
((macroexp--compiling-p)
diff --git a/lisp/emacs-lisp/map-ynp.el b/lisp/emacs-lisp/map-ynp.el
index d390a0d69a7..af7a9ee4abb 100644
--- a/lisp/emacs-lisp/map-ynp.el
+++ b/lisp/emacs-lisp/map-ynp.el
@@ -144,8 +144,7 @@ Returns the number of actions taken."
(cons prompt map))
'quit))
;; Prompt in the echo area.
- (let ((cursor-in-echo-area (not no-cursor-in-echo-area))
- (message-log-max nil))
+ (let ((cursor-in-echo-area (not no-cursor-in-echo-area)))
(message (apply 'propertize "%s(y, n, !, ., q, %sor %s) "
minibuffer-prompt-properties)
prompt user-keys
diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el
index 6371ec37906..a89457e877d 100644
--- a/lisp/emacs-lisp/map.el
+++ b/lisp/emacs-lisp/map.el
@@ -4,7 +4,7 @@
;; Author: Nicolas Petton <nicolas@petton.fr>
;; Keywords: convenience, map, hash-table, alist, array
-;; Version: 1.0
+;; Version: 1.1
;; Package: map
;; Maintainer: emacs-devel@gnu.org
@@ -43,6 +43,7 @@
;;; Code:
(require 'seq)
+(eval-when-compile (require 'cl-lib))
(pcase-defmacro map (&rest args)
"Build a `pcase' pattern matching map elements.
@@ -78,14 +79,14 @@ MAP can be a list, hash-table or array."
(eval-when-compile
(defmacro map--dispatch (map-var &rest args)
- "Evaluate one of the forms specified by ARGS based on the type of MAP.
+ "Evaluate one of the forms specified by ARGS based on the type of MAP-VAR.
The following keyword types are meaningful: `:list',
`:hash-table' and `:array'.
-An error is thrown if MAP is neither a list, hash-table nor array.
+An error is thrown if MAP-VAR is neither a list, hash-table nor array.
-Return RESULT if non-nil or the result of evaluation of the form."
+Returns the result of evaluating the form associated with MAP-VAR's type."
(declare (debug t) (indent 1))
`(cond ((listp ,map-var) ,(plist-get args :list))
((hash-table-p ,map-var) ,(plist-get args :hash-table))
@@ -200,6 +201,16 @@ MAP can be a list, hash-table or array."
function
map))
+(defun map-do (function map)
+ "Apply FUNCTION to each element of MAP and return nil.
+FUNCTION.is called with two arguments, the key and the value."
+ (funcall (map--dispatch map
+ :list #'map--do-alist
+ :hash-table #'maphash
+ :array #'map--do-array)
+ function
+ map))
+
(defun map-keys-apply (function map)
"Return the result of applying FUNCTION to each key of MAP.
@@ -249,7 +260,7 @@ MAP can be a list, hash-table or array."
:hash-table (zerop (hash-table-count map))))
(defun map-contains-key (map key &optional testfn)
- "Return non-nil if MAP contain KEY, nil otherwise.
+ "If MAP contain KEY return KEY, nil otherwise.
Equality is defined by TESTFN if non-nil or by `equal' if nil.
MAP can be a list, hash-table or array."
@@ -282,27 +293,33 @@ MAP can be a list, hash-table or array."
"Merge into a map of type TYPE all the key/value pairs in MAPS.
MAP can be a list, hash-table or array."
- (let (result)
+ (let ((result (map-into (pop maps) type)))
(while maps
+ ;; FIXME: When `type' is `list', we get an O(N^2) behavior.
+ ;; For small tables, this is fine, but for large tables, we
+ ;; should probably use a hash-table internally which we convert
+ ;; to an alist in the end.
(map-apply (lambda (key value)
- (setf (map-elt result key) value))
- (pop maps)))
- (map-into result type)))
+ (setf (map-elt result key) value))
+ (pop maps)))
+ result))
(defun map-merge-with (type function &rest maps)
"Merge into a map of type TYPE all the key/value pairs in MAPS.
When two maps contain the same key, call FUNCTION on the two
values and use the value returned by it.
MAP can be a list, hash-table or array."
- (let (result)
+ (let ((result (map-into (pop maps) type))
+ (not-found (cons nil nil)))
(while maps
(map-apply (lambda (key value)
- (setf (map-elt result key)
- (if (map-contains-key result key)
- (funcall function (map-elt result key) value)
- value)))
- (pop maps)))
- (map-into result type)))
+ (cl-callf (lambda (old)
+ (if (eq old not-found)
+ value
+ (funcall function old value)))
+ (map-elt result key not-found)))
+ (pop maps)))
+ result))
(defun map-into (map type)
"Convert the map MAP into a map of type TYPE.
@@ -347,6 +364,20 @@ MAP can be a list, hash-table or array."
(setq index (1+ index))))
map)))
+(defun map--do-alist (function alist)
+ "Private function used to iterate over ALIST using FUNCTION."
+ (seq-do (lambda (pair)
+ (funcall function
+ (car pair)
+ (cdr pair)))
+ alist))
+
+(defun map--do-array (function array)
+ "Private function used to iterate over ARRAY using FUNCTION."
+ (seq-do-indexed (lambda (elt index)
+ (funcall function index elt))
+ array))
+
(defun map--into-hash-table (map)
"Convert MAP into a hash-table."
(let ((ht (make-hash-table :size (map-length map)
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index 5a100b790f1..fd1cd2c7aaf 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -72,6 +72,13 @@ Each element has the form (WHERE BYTECODE STACK) where:
(setq f (advice--cdr f)))
f)
+(defun advice--where (f)
+ (let ((bytecode (aref f 1))
+ (where nil))
+ (dolist (elem advice--where-alist)
+ (if (eq bytecode (cadr elem)) (setq where (car elem))))
+ where))
+
(defun advice--make-docstring (function)
"Build the raw docstring for FUNCTION, presumably advised."
(let* ((flist (indirect-function function))
@@ -79,16 +86,13 @@ Each element has the form (WHERE BYTECODE STACK) where:
(docstring nil))
(if (eq 'macro (car-safe flist)) (setq flist (cdr flist)))
(while (advice--p flist)
- (let ((bytecode (aref flist 1))
- (doc (aref flist 4))
- (where nil))
+ (let ((doc (aref flist 4))
+ (where (advice--where flist)))
;; Hack attack! For advices installed before calling
;; Snarf-documentation, the integer offset into the DOC file will not
;; be installed in the "core unadvised function" but in the advice
;; object instead! So here we try to undo the damage.
(if (integerp doc) (setq docfun flist))
- (dolist (elem advice--where-alist)
- (if (eq bytecode (cadr elem)) (setq where (car elem))))
(setq docstring
(concat
docstring
@@ -502,6 +506,10 @@ of the piece of advice."
(setq frame2 (backtrace-frame i #'called-interactively-p))
;; (message "Advice Frame %d = %S" i frame2)
(setq i (1+ i)))))
+ ;; FIXME: Adjust this for the new :filter advices, since they use `funcall'
+ ;; rather than `apply'.
+ ;; FIXME: Somehow this doesn't work on (advice-add :before
+ ;; 'call-interactively #'ignore), see bug#3984.
(when (and (eq (nth 1 frame2) 'apply)
(progn
(funcall get-next-frame)
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 32200227de9..bebfd18d7a6 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -150,6 +150,7 @@
(require 'tabulated-list)
(require 'macroexp)
+(require 'url-handlers)
(defgroup package nil
"Manager for Emacs Lisp packages."
@@ -193,14 +194,16 @@ If VERSION is nil, the package is not loaded (it is \"disabled\")."
:risky t
:version "24.1")
-(defcustom package-archives '(("gnu" . "http://elpa.gnu.org/packages/"))
+(defcustom package-archives `(("gnu" .
+ ,(format "http%s://elpa.gnu.org/packages/"
+ (if (gnutls-available-p) "s" ""))))
"An alist of archives from which to fetch.
The default value points to the GNU Emacs package repository.
Each element has the form (ID . LOCATION).
ID is an archive name, as a string.
LOCATION specifies the base location for the archive.
- If it starts with \"http:\", it is treated as a HTTP URL;
+ If it starts with \"http(s):\", it is treated as an HTTP(S) URL;
otherwise it should be an absolute directory name.
(Other types of URL are currently not supported.)
@@ -209,7 +212,7 @@ a package can run arbitrary code."
:type '(alist :key-type (string :tag "Archive name")
:value-type (string :tag "URL or directory name"))
:risky t
- :version "24.1")
+ :version "26.1") ; gnutls test
(defcustom package-menu-hide-low-priority 'archive
"If non-nil, hide low priority packages from the packages menu.
@@ -302,7 +305,24 @@ contrast, `package-user-dir' contains packages for personal use."
:version "24.1")
(declare-function epg-find-configuration "epg-config"
- (protocol &optional force))
+ (protocol &optional no-cache program-alist))
+
+(defcustom package-gnupghome-dir (expand-file-name "gnupg" package-user-dir)
+ "Directory containing GnuPG keyring or nil.
+This variable specifies the GnuPG home directory used by package.
+That directory is passed via the option \"--homedir\" to GnuPG.
+If nil, do not use the option \"--homedir\", but stick with GnuPG's
+default directory."
+ :type `(choice
+ (const
+ :tag "Default Emacs package management GnuPG home directory"
+ ,(expand-file-name "gnupg" package-user-dir))
+ (const
+ :tag "Default GnuPG directory (GnuPG option --homedir not used)"
+ nil)
+ (directory :tag "A specific GnuPG --homedir"))
+ :risky t
+ :version "26.1")
(defcustom package-check-signature
(if (and (require 'epg-config)
@@ -618,7 +638,7 @@ Return the max version (as a string) if the package is held at a lower version."
(t (error "Invalid element in `package-load-list'")))))
(defun package-built-in-p (package &optional min-version)
- "Return true if PACKAGE is built-in to Emacs.
+ "Return non-nil if PACKAGE is built-in to Emacs.
Optional arg MIN-VERSION, if non-nil, should be a version list
specifying the minimum acceptable version."
(if (package-desc-p package) ;; was built-in and then was converted
@@ -791,7 +811,7 @@ untar into a directory named DIR; otherwise, signal an error."
(tar-mode)
;; Make sure everything extracts into DIR.
(let ((regexp (concat "\\`" (regexp-quote (expand-file-name dir)) "/"))
- (case-fold-search (memq system-type '(windows-nt ms-dos cygwin))))
+ (case-fold-search (file-name-case-insensitive-p dir)))
(dolist (tar-data tar-parse-info)
(let ((name (expand-file-name (tar-header-name tar-data))))
(or (string-match regexp name)
@@ -885,34 +905,25 @@ untar into a directory named DIR; otherwise, signal an error."
nil pkg-file nil 'silent))))
;;;; Autoload
-;; From Emacs 22, but changed so it adds to load-path.
+(declare-function autoload-rubric "autoload" (file &optional type feature))
+
(defun package-autoload-ensure-default-file (file)
"Make sure that the autoload file FILE exists and if not create it."
(unless (file-exists-p file)
- (write-region
- (concat ";;; " (file-name-nondirectory file)
- " --- automatically extracted autoloads\n"
- ";;\n"
- ";;; Code:\n"
- ;; `load-path' should contain only directory names
- "(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))\n"
- " \n;; Local Variables:\n"
- ";; version-control: never\n"
- ";; no-byte-compile: t\n"
- ";; no-update-autoloads: t\n"
- ";; End:\n"
- ";;; " (file-name-nondirectory file)
- " ends here\n")
- nil file nil 'silent))
+ (require 'autoload)
+ (write-region (autoload-rubric file "package" nil) nil file nil 'silent))
file)
(defvar generated-autoload-file)
+(defvar autoload-timestamps)
(defvar version-control)
(defun package-generate-autoloads (name pkg-dir)
(let* ((auto-name (format "%s-autoloads.el" name))
;;(ignore-name (concat name "-pkg.el"))
(generated-autoload-file (expand-file-name auto-name pkg-dir))
+ ;; We don't need 'em, and this makes the output reproducible.
+ (autoload-timestamps nil)
;; Silence `autoload-generate-file-autoloads'.
(noninteractive inhibit-message)
(backup-inhibited t)
@@ -1077,6 +1088,8 @@ The return result is a `package-desc'."
(setq files nil)
;; set the 'dir kind,
(setf (package-desc-kind info) 'dir))))
+ (unless info
+ (error "No .el files with package headers in `%s'" default-directory))
;; and return the info.
info))))
@@ -1201,9 +1214,9 @@ errors signaled by ERROR-FORM or by BODY).
"Check signature CONTENT against STRING.
SIG-FILE is the name of the signature file, used when signaling
errors."
- (let* ((context (epg-make-context 'OpenPGP))
- (homedir (expand-file-name "gnupg" package-user-dir)))
- (setf (epg-context-home-directory context) homedir)
+ (let ((context (epg-make-context 'OpenPGP)))
+ (when package-gnupghome-dir
+ (setf (epg-context-home-directory context) package-gnupghome-dir))
(condition-case error
(epg-verify-string context content string)
(error (package--display-verify-error context sig-file)
@@ -1230,7 +1243,7 @@ errors."
"Check signature of the current buffer.
Download the signature file from LOCATION by appending \".sig\"
to FILE.
-GnuPG keyring is located under \"gnupg\" in `package-user-dir'.
+GnuPG keyring location depends on `package-gnupghome-dir'.
STRING is the string to verify, it defaults to `buffer-string'.
If ASYNC is non-nil, the download of the signature file is
done asynchronously.
@@ -1439,13 +1452,13 @@ individual packages after calling `package-initialize' -- this is
taken care of by `package-initialize'."
(interactive)
(setq package-alist nil)
- (if (equal user-init-file load-file-name)
- ;; If `package-initialize' is being called as part of loading
- ;; the init file, it's obvious we don't need to ensure-init.
- (setq package--init-file-ensured t
- ;; And likely we don't need to run it again after init.
- package-enable-at-startup nil)
- (package--ensure-init-file))
+ (if after-init-time
+ (package--ensure-init-file)
+ ;; If `package-initialize' is before we finished loading the init
+ ;; file, it's obvious we don't need to ensure-init.
+ (setq package--init-file-ensured t
+ ;; And likely we don't need to run it again after init.
+ package-enable-at-startup nil))
(package-load-all-descriptors)
(package-read-all-archive-contents)
(unless no-activate
@@ -1470,11 +1483,11 @@ taken care of by `package-initialize'."
"Import keys from FILE."
(interactive "fFile: ")
(setq file (expand-file-name file))
- (let ((context (epg-make-context 'OpenPGP))
- (homedir (expand-file-name "gnupg" package-user-dir)))
- (with-file-modes 448
- (make-directory homedir t))
- (setf (epg-context-home-directory context) homedir)
+ (let ((context (epg-make-context 'OpenPGP)))
+ (when package-gnupghome-dir
+ (with-file-modes 448
+ (make-directory package-gnupghome-dir t))
+ (setf (epg-context-home-directory context) package-gnupghome-dir))
(message "Importing %s..." (file-name-nondirectory file))
(epg-import-keys-from-file context file)
(message "Importing %s...done" (file-name-nondirectory file))))
@@ -1516,7 +1529,7 @@ similar to an entry in `package-alist'. Save the cached copy to
(when (listp (read-from-string content))
(make-directory dir t)
(if (or (not package-check-signature)
- (member archive package-unsigned-archives))
+ (member name package-unsigned-archives))
;; If we don't care about the signature, save the file and
;; we're done.
(progn (write-region content nil local-file nil 'silent)
@@ -1763,7 +1776,7 @@ destructively set to nil in ONLY."
That is, any element of the returned list is guaranteed to not
directly depend on any elements that come before it.
-PACKAGE-LIST is a list of package-desc objects.
+PACKAGE-LIST is a list of `package-desc' objects.
Indirect dependencies are guaranteed to be returned in order only
if all the in-between dependencies are also in PACKAGE-LIST."
(let ((alist (mapcar (lambda (p) (cons (package-desc-name p) p)) package-list))
@@ -1832,11 +1845,11 @@ if all the in-between dependencies are also in PACKAGE-LIST."
(setf (package-desc-signed (car pkg-descs)) t))))))))))
(defun package-installed-p (package &optional min-version)
- "Return true if PACKAGE, of MIN-VERSION or newer, is installed.
+ "Return non-nil if PACKAGE, of MIN-VERSION or newer, is installed.
If PACKAGE is a symbol, it is the package name and MIN-VERSION
should be a version list.
-If PACKAGE is a package-desc object, MIN-VERSION is ignored."
+If PACKAGE is a `package-desc' object, MIN-VERSION is ignored."
(unless package--initialized (error "package.el is not yet initialized!"))
(if (package-desc-p package)
(let ((dir (package-desc-dir package)))
@@ -1852,7 +1865,7 @@ If PACKAGE is a package-desc object, MIN-VERSION is ignored."
(defun package-download-transaction (packages)
"Download and install all the packages in PACKAGES.
-PACKAGES should be a list of package-desc.
+PACKAGES should be a list of `package-desc'.
This function assumes that all package requirements in
PACKAGES are satisfied, i.e. that PACKAGES is computed
using `package-compute-transaction'."
@@ -1919,13 +1932,13 @@ add a call to it along with some explanatory comments."
;;;###autoload
(defun package-install (pkg &optional dont-select)
"Install the package PKG.
-PKG can be a package-desc or a symbol naming one of the available packages
+PKG can be a `package-desc' or a symbol naming one of the available packages
in an archive in `package-archives'. Interactively, prompt for its name.
If called interactively or if DONT-SELECT nil, add PKG to
`package-selected-packages'.
-If PKG is a package-desc and it is already installed, don't try
+If PKG is a `package-desc' and it is already installed, don't try
to install it but still mark it as selected."
(interactive
(progn
@@ -2054,7 +2067,7 @@ If some packages are not installed propose to install them."
;;; Package Deletion
(defun package--newest-p (pkg)
- "Return t if PKG is the newest package with its name."
+ "Return non-nil if PKG is the newest package with its name."
(equal (cadr (assq (package-desc-name pkg) package-alist))
pkg))
@@ -2115,10 +2128,15 @@ If NOSAVE is non-nil, the package is not removed from
(t
(add-hook 'post-command-hook #'package-menu--post-refresh)
(delete-directory dir t t)
- ;; Remove NAME-VERSION.signed file.
- (let ((signed-file (concat dir ".signed")))
- (if (file-exists-p signed-file)
- (delete-file signed-file)))
+ ;; Remove NAME-VERSION.signed and NAME-readme.txt files.
+ (dolist (suffix '(".signed" "readme.txt"))
+ (let* ((version (package-version-join (package-desc-version pkg-desc)))
+ (file (concat (if (string= suffix ".signed")
+ dir
+ (substring dir 0 (- (length version))))
+ suffix)))
+ (when (file-exists-p file)
+ (delete-file file))))
;; Update package-alist.
(let ((pkgs (assq name package-alist)))
(delete pkg-desc pkgs)
@@ -2129,7 +2147,7 @@ If NOSAVE is non-nil, the package is not removed from
;;;###autoload
(defun package-reinstall (pkg)
"Reinstall package PKG.
-PKG should be either a symbol, the package name, or a package-desc
+PKG should be either a symbol, the package name, or a `package-desc'
object."
(interactive (list (intern (completing-read
"Reinstall package: "
@@ -2304,7 +2322,7 @@ Otherwise no newline is inserted."
(insert "\n")
(unless (and pkg-dir (not archive)) ; Installed pkgs don't have archive.
(package--print-help-section "Archive"
- (or archive "n/a") "\n"))
+ (or archive "n/a")))
(and version
(package--print-help-section "Version"
(package-version-join version)))
@@ -2346,6 +2364,12 @@ Otherwise no newline is inserted."
(package-desc-name pkg))))
(insert "\n")))
(when homepage
+ ;; Prefer https for the homepage of packages on gnu.org.
+ (if (string-match-p "^http://\\(elpa\\|www\\)\\.gnu\\.org/" homepage)
+ (let ((gnu (cdr (assoc "gnu" package-archives))))
+ (and gnu (string-match-p "^https" gnu)
+ (setq homepage
+ (replace-regexp-in-string "^http" "https" homepage)))))
(package--print-help-section "Homepage")
(help-insert-xref-button homepage 'help-url homepage)
(insert "\n"))
@@ -2548,7 +2572,7 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC."
(defun package--incompatible-p (pkg &optional shallow)
"Return non-nil if PKG has no chance of being installable.
-PKG is a package-desc object.
+PKG is a `package-desc' object.
If SHALLOW is non-nil, this only checks if PKG depends on a
higher `emacs-version' than the one being used. Otherwise, also
@@ -2632,7 +2656,7 @@ Installed obsolete packages are always displayed.")
(defun package--remove-hidden (pkg-list)
"Filter PKG-LIST according to `package-archive-priorities'.
-PKG-LIST must be a list of package-desc objects, all with the
+PKG-LIST must be a list of `package-desc' objects, all with the
same name, sorted by decreasing `package-desc-priority-version'.
Return a list of packages tied for the highest priority according
to their archives."
@@ -2873,7 +2897,7 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
:version "25.1")
(defface package-status-incompat
- '((t :inherit font-lock-comment-face))
+ '((t :inherit error))
"Face used on the status and version of incompat packages."
:version "25.1")
@@ -2886,7 +2910,7 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
;;; Package menu printing
(defun package-menu--print-info-simple (pkg)
"Return a package entry suitable for `tabulated-list-entries'.
-PKG is a package-desc object.
+PKG is a `package-desc' object.
Return (PKG-DESC [NAME VERSION STATUS DOC])."
(let* ((status (package-desc-status pkg))
(face (pcase status
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 379b3ca69ba..4a06ab25d3e 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -89,7 +89,8 @@
(functionp &rest form)
sexp))
-(def-edebug-spec pcase-MACRO pcase--edebug-match-macro)
+;; See bug#24717
+(put 'pcase-MACRO 'edebug-form-spec 'pcase--edebug-match-macro)
;; Only called from edebug.
(declare-function get-edebug-spec "edebug" (symbol))
@@ -174,8 +175,8 @@ Emacs Lisp manual for more information and examples."
;; (when (gethash (car cases) pcase--memoize-2)
;; (message "pcase-memoize failed because of eq test on %S"
;; (car cases)))
- (when data
- (message "pcase-memoize: equal first branch, yet different"))
+ ;; (when data
+ ;; (message "pcase-memoize: equal first branch, yet different"))
(let ((expansion (pcase--expand exp cases)))
(puthash (car cases) `(,exp ,cases ,@expansion) pcase--memoize)
;; (puthash (car cases) `(,exp ,cases ,@expansion) pcase--memoize-1)
@@ -298,6 +299,8 @@ any kind of error."
;;;###autoload
(defmacro pcase-dolist (spec &rest body)
+ "Like `dolist' but where the binding can be a `pcase' pattern.
+\n(fn (PATTERN LIST) BODY...)"
(declare (indent 1) (debug ((pcase-PAT form) body)))
(if (pcase--trivial-upat-p (car spec))
`(dolist ,spec ,@body)
@@ -434,8 +437,10 @@ to this macro."
;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding
;; let* which might prevent the setcar/setcdr in pcase--expand's fancy
;; codegen from later metamorphosing this let into a funcall.
- `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars)
- ,@code))
+ (if vars
+ `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars)
+ ,@code)
+ `(progn ,@code)))
(defun pcase--small-branch-p (code)
(and (= 1 (length code))
@@ -498,23 +503,30 @@ MATCH is the pattern that needs to be matched, of the form:
(symbolp . vectorp)
(symbolp . stringp)
(symbolp . byte-code-function-p)
+ (symbolp . recordp)
(integerp . consp)
(integerp . arrayp)
(integerp . vectorp)
(integerp . stringp)
(integerp . byte-code-function-p)
+ (integerp . recordp)
(numberp . consp)
(numberp . arrayp)
(numberp . vectorp)
(numberp . stringp)
(numberp . byte-code-function-p)
+ (numberp . recordp)
(consp . arrayp)
+ (consp . atom)
(consp . vectorp)
(consp . stringp)
(consp . byte-code-function-p)
+ (consp . recordp)
(arrayp . byte-code-function-p)
(vectorp . byte-code-function-p)
+ (vectorp . recordp)
(stringp . vectorp)
+ (stringp . recordp)
(stringp . byte-code-function-p)))
(defun pcase--mutually-exclusive-p (pred1 pred2)
diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el
index 2938c37e8a8..7ef46a48bde 100644
--- a/lisp/emacs-lisp/pp.el
+++ b/lisp/emacs-lisp/pp.el
@@ -1,4 +1,4 @@
-;;; pp.el --- pretty printer for Emacs Lisp
+;;; pp.el --- pretty printer for Emacs Lisp -*- lexical-binding: t -*-
;; Copyright (C) 1989, 1993, 2001-2017 Free Software Foundation, Inc.
@@ -67,8 +67,7 @@ to make output that `read' can handle, whenever this is possible."
(progn (skip-chars-backward " \t\n") (point)))
(insert "\n"))))
((ignore-errors (up-list 1) t)
- (while (looking-at-p "\\s)")
- (forward-char 1))
+ (skip-syntax-forward ")")
(delete-region
(point)
(progn (skip-chars-forward " \t\n") (point)))
@@ -129,7 +128,7 @@ Also add the value to the front of the list in the variable `values'."
(interactive
(list (read--expression "Eval: ")))
(message "Evaluating...")
- (setq values (cons (eval expression lexical-binding) values))
+ (push (eval expression lexical-binding) values)
(pp-display-expression (car values) "*Pp Eval Output*"))
;;;###autoload
@@ -141,22 +140,21 @@ Also add the value to the front of the list in the variable `values'."
(defun pp-last-sexp ()
"Read sexp before point. Ignores leading comment characters."
- (let ((stab (syntax-table)) (pt (point)) start exp)
- (set-syntax-table emacs-lisp-mode-syntax-table)
- (save-excursion
- (forward-sexp -1)
- ;; If first line is commented, ignore all leading comments:
- (if (save-excursion (beginning-of-line) (looking-at-p "[ \t]*;"))
- (progn
- (setq exp (buffer-substring (point) pt))
- (while (string-match "\n[ \t]*;+" exp start)
- (setq start (1+ (match-beginning 0))
- exp (concat (substring exp 0 start)
- (substring exp (match-end 0)))))
- (setq exp (read exp)))
- (setq exp (read (current-buffer)))))
- (set-syntax-table stab)
- exp))
+ (with-syntax-table emacs-lisp-mode-syntax-table
+ (let ((pt (point)))
+ (save-excursion
+ (forward-sexp -1)
+ (read
+ ;; If first line is commented, ignore all leading comments:
+ (if (save-excursion (beginning-of-line) (looking-at-p "[ \t]*;"))
+ (let ((exp (buffer-substring (point) pt))
+ (start nil))
+ (while (string-match "\n[ \t]*;+" exp start)
+ (setq start (1+ (match-beginning 0))
+ exp (concat (substring exp 0 start)
+ (substring exp (match-end 0)))))
+ exp)
+ (current-buffer)))))))
;;;###autoload
(defun pp-eval-last-sexp (arg)
@@ -178,19 +176,6 @@ Ignores leading comment characters."
(insert (pp-to-string (macroexpand-1 (pp-last-sexp))))
(pp-macroexpand-expression (pp-last-sexp))))
-;;; Test cases for quote
-;; (pp-eval-expression ''(quote quote))
-;; (pp-eval-expression ''((quote a) (quote b)))
-;; (pp-eval-expression ''('a 'b)) ; same as above
-;; (pp-eval-expression ''((quote (quote quote)) (quote quote)))
-;; These do not satisfy the quote test.
-;; (pp-eval-expression ''quote)
-;; (pp-eval-expression ''(quote))
-;; (pp-eval-expression ''(quote . quote))
-;; (pp-eval-expression ''(quote a b))
-;; (pp-eval-expression ''(quotefoo))
-;; (pp-eval-expression ''(a b))
-
(provide 'pp) ; so (require 'pp) works
;;; pp.el ends here
diff --git a/lisp/emacs-lisp/radix-tree.el b/lisp/emacs-lisp/radix-tree.el
new file mode 100644
index 00000000000..b5e7589b951
--- /dev/null
+++ b/lisp/emacs-lisp/radix-tree.el
@@ -0,0 +1,246 @@
+;;; radix-tree.el --- A simple library of radix trees -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2016-2017 Free Software Foundation, Inc.
+
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; Keywords:
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; There are many different options for how to represent radix trees
+;; in Elisp. Here I chose a very simple one. A radix-tree can be either:
+;; - a node, of the form ((PREFIX . PTREE) . RTREE) where PREFIX is a string
+;; meaning that everything that starts with PREFIX is in PTREE,
+;; and everything else in RTREE. It also has the property that
+;; everything that starts with the first letter of PREFIX but not with
+;; that whole PREFIX is not in RTREE (i.e. is not in the tree at all).
+;; - anything else is taken as the value to associate with the empty string.
+;; So every node is basically an (improper) alist where each mapping applies
+;; to a different leading letter.
+;;
+;; The main downside of this representation is that the lookup operation
+;; is slower because each level of the tree is an alist rather than some kind
+;; of array, so every level's lookup is O(N) rather than O(1). We could easily
+;; solve this by using char-tables instead of alists, but that would make every
+;; level take up a lot more memory, and it would make the resulting
+;; data structure harder to read (by a human) when printed out.
+
+;;; Code:
+
+(defun radix-tree--insert (tree key val i)
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (let* ((ni (+ i (length prefix)))
+ (cmp (compare-strings prefix nil nil key i ni)))
+ (if (eq t cmp)
+ (let ((nptree (radix-tree--insert ptree key val ni)))
+ `((,prefix . ,nptree) . ,rtree))
+ (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
+ (if (zerop n)
+ (let ((nrtree (radix-tree--insert rtree key val i)))
+ `((,prefix . ,ptree) . ,nrtree))
+ (let* ((nprefix (substring prefix 0 n))
+ (kprefix (substring key (+ i n)))
+ (pprefix (substring prefix n))
+ (ktree (if (equal kprefix "") val
+ `((,kprefix . ,val)))))
+ `((,nprefix
+ . ((,pprefix . ,ptree) . ,ktree))
+ . ,rtree)))))))
+ (_
+ (if (= (length key) i) val
+ (let ((prefix (substring key i)))
+ `((,prefix . ,val) . ,tree))))))
+
+(defun radix-tree--remove (tree key i)
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (let* ((ni (+ i (length prefix)))
+ (cmp (compare-strings prefix nil nil key i ni)))
+ (if (eq t cmp)
+ (pcase (radix-tree--remove ptree key ni)
+ (`nil rtree)
+ (`((,pprefix . ,pptree))
+ `((,(concat prefix pprefix) . ,pptree) . ,rtree))
+ (nptree `((,prefix . ,nptree) . ,rtree)))
+ (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
+ (if (zerop n)
+ (let ((nrtree (radix-tree--remove rtree key i)))
+ `((,prefix . ,ptree) . ,nrtree))
+ tree)))))
+ (_
+ (if (= (length key) i) nil tree))))
+
+
+(defun radix-tree--lookup (tree string i)
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (let* ((ni (+ i (length prefix)))
+ (cmp (compare-strings prefix nil nil string i ni)))
+ (if (eq t cmp)
+ (radix-tree--lookup ptree string ni)
+ (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
+ (if (zerop n)
+ (radix-tree--lookup rtree string i)
+ (+ i n))))))
+ (val
+ (if (and val (equal (length string) i))
+ (if (integerp val) `(t . ,val) val)
+ i))))
+
+;; (defun radix-tree--trim (tree string i)
+;; (if (= i (length string))
+;; tree
+;; (pcase tree
+;; (`((,prefix . ,ptree) . ,rtree)
+;; (let* ((ni (+ i (length prefix)))
+;; (cmp (compare-strings prefix nil nil string i ni))
+;; ;; FIXME: We could compute nrtree more efficiently
+;; ;; whenever cmp is not -1 or 1.
+;; (nrtree (radix-tree--trim rtree string i)))
+;; (if (eq t cmp)
+;; (pcase (radix-tree--trim ptree string ni)
+;; (`nil nrtree)
+;; (`((,pprefix . ,pptree))
+;; `((,(concat prefix pprefix) . ,pptree) . ,nrtree))
+;; (nptree `((,prefix . ,nptree) . ,nrtree)))
+;; (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
+;; (cond
+;; ((equal (+ n i) (length string))
+;; `((,prefix . ,ptree) . ,nrtree))
+;; (t nrtree))))))
+;; (val val))))
+
+(defun radix-tree--prefixes (tree string i prefixes)
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (let* ((ni (+ i (length prefix)))
+ (cmp (compare-strings prefix nil nil string i ni))
+ ;; FIXME: We could compute prefixes more efficiently
+ ;; whenever cmp is not -1 or 1.
+ (prefixes (radix-tree--prefixes rtree string i prefixes)))
+ (if (eq t cmp)
+ (radix-tree--prefixes ptree string ni prefixes)
+ prefixes)))
+ (val
+ (if (null val)
+ prefixes
+ (cons (cons (substring string 0 i)
+ (if (eq (car-safe val) t) (cdr val) val))
+ prefixes)))))
+
+(defun radix-tree--subtree (tree string i)
+ (if (equal (length string) i) tree
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (let* ((ni (+ i (length prefix)))
+ (cmp (compare-strings prefix nil nil string i ni)))
+ (if (eq t cmp)
+ (radix-tree--subtree ptree string ni)
+ (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
+ (cond
+ ((zerop n) (radix-tree--subtree rtree string i))
+ ((equal (+ n i) (length string))
+ (let ((nprefix (substring prefix n)))
+ `((,nprefix . ,ptree))))
+ (t nil))))))
+ (_ nil))))
+
+;;; Entry points
+
+(defconst radix-tree-empty nil
+ "The empty radix-tree.")
+
+(defun radix-tree-insert (tree key val)
+ "Insert a mapping from KEY to VAL in radix TREE."
+ (when (consp val) (setq val `(t . ,val)))
+ (if val (radix-tree--insert tree key val 0)
+ (radix-tree--remove tree key 0)))
+
+(defun radix-tree-lookup (tree key)
+ "Return the value associated to KEY in radix TREE.
+If not found, return nil."
+ (pcase (radix-tree--lookup tree key 0)
+ (`(t . ,val) val)
+ ((pred numberp) nil)
+ (val val)))
+
+(defun radix-tree-subtree (tree string)
+ "Return the subtree of TREE rooted at the prefix STRING."
+ (radix-tree--subtree tree string 0))
+
+;; (defun radix-tree-trim (tree string)
+;; "Return a TREE which only holds entries \"related\" to STRING.
+;; \"Related\" is here defined as entries where there's a `string-prefix-p' relation
+;; between STRING and the key."
+;; (radix-tree-trim tree string 0))
+
+(defun radix-tree-prefixes (tree string)
+ "Return an alist of all bindings in TREE for prefixes of STRING."
+ (radix-tree--prefixes tree string 0 nil))
+
+(eval-and-compile
+ (pcase-defmacro radix-tree-leaf (vpat)
+ ;; FIXME: We'd like to use a negative pattern (not consp), but pcase
+ ;; doesn't support it. Using `atom' works but generates sub-optimal code.
+ `(or `(t . ,,vpat) (and (pred atom) ,vpat))))
+
+(defun radix-tree-iter-subtrees (tree fun)
+ "Apply FUN to every immediate subtree of radix TREE.
+FUN is called with two arguments: PREFIX and SUBTREE.
+You can test if SUBTREE is a leaf (and extract its value) with the
+pcase pattern (radix-tree-leaf PAT)."
+ (while tree
+ (pcase tree
+ (`((,prefix . ,ptree) . ,rtree)
+ (funcall fun prefix ptree)
+ (setq tree rtree))
+ (_ (funcall fun "" tree)
+ (setq tree nil)))))
+
+(defun radix-tree-iter-mappings (tree fun &optional prefix)
+ "Apply FUN to every mapping in TREE.
+FUN is called with two arguments: KEY and VAL.
+PREFIX is only used internally."
+ (radix-tree-iter-subtrees
+ tree
+ (lambda (p s)
+ (let ((nprefix (concat prefix p)))
+ (pcase s
+ ((radix-tree-leaf v) (funcall fun nprefix v))
+ (_ (radix-tree-iter-mappings s fun nprefix)))))))
+
+;; (defun radix-tree->alist (tree)
+;; (let ((al nil))
+;; (radix-tree-iter-mappings tree (lambda (p v) (push (cons p v) al)))
+;; al))
+
+(defun radix-tree-count (tree)
+ (let ((i 0))
+ (radix-tree-iter-mappings tree (lambda (_k _v) (setq i (1+ i))))
+ i))
+
+(defun radix-tree-from-map (map)
+ ;; Aka (cl-defmethod map-into (map (type (eql radix-tree)))) ...)
+ (require 'map)
+ (let ((rt nil))
+ (map-apply (lambda (k v) (setq rt (radix-tree-insert rt k v))) map)
+ rt))
+
+(provide 'radix-tree)
+;;; radix-tree.el ends here
diff --git a/lisp/emacs-lisp/re-builder.el b/lisp/emacs-lisp/re-builder.el
index 5264dae52ae..f60d723a883 100644
--- a/lisp/emacs-lisp/re-builder.el
+++ b/lisp/emacs-lisp/re-builder.el
@@ -488,10 +488,10 @@ If the optional PAUSE is non-nil then pause at the end in any case."
Optional argument SYNTAX must be specified if called non-interactively."
(interactive
(list (intern
- (completing-read "Select syntax: "
- (mapcar (lambda (el) (cons (symbol-name el) 1))
- '(read string sregex rx))
- nil t (symbol-name reb-re-syntax)))))
+ (completing-read
+ (format "Select syntax (default %s): " reb-re-syntax)
+ '(read string sregex rx)
+ nil t nil nil (symbol-name reb-re-syntax)))))
(if (memq syntax '(read string sregex rx))
(let ((buffer (get-buffer reb-buffer)))
diff --git a/lisp/emacs-lisp/regexp-opt.el b/lisp/emacs-lisp/regexp-opt.el
index 8b91668c8c2..5feaad88c7b 100644
--- a/lisp/emacs-lisp/regexp-opt.el
+++ b/lisp/emacs-lisp/regexp-opt.el
@@ -1,4 +1,4 @@
-;;; regexp-opt.el --- generate efficient regexps to match strings
+;;; regexp-opt.el --- generate efficient regexps to match strings -*- lexical-binding: t -*-
;; Copyright (C) 1994-2017 Free Software Foundation, Inc.
@@ -262,7 +262,7 @@ CHARS should be a list of characters."
;; The basic idea is to find character ranges. Also we take care in the
;; position of character set meta characters in the character set regexp.
;;
- (let* ((charmap (make-char-table 'case-table))
+ (let* ((charmap (make-char-table 'regexp-opt-charset))
(start -1) (end -2)
(charset "")
(bracket "") (dash "") (caret ""))
diff --git a/lisp/emacs-lisp/ring.el b/lisp/emacs-lisp/ring.el
index 371723fa0b5..b0ec3bcbe01 100644
--- a/lisp/emacs-lisp/ring.el
+++ b/lisp/emacs-lisp/ring.el
@@ -1,4 +1,4 @@
-;;; ring.el --- handle rings of items
+;;; ring.el --- handle rings of items -*- lexical-binding: t; -*-
;; Copyright (C) 1992, 2001-2017 Free Software Foundation, Inc.
@@ -160,14 +160,15 @@ will be performed."
(size (ring-size ring))
(vect (cddr ring))
lst)
- (dotimes (var (cadr ring) lst)
- (push (aref vect (mod (+ start var) size)) lst))))
+ (dotimes (var (cadr ring))
+ (push (aref vect (mod (+ start var) size)) lst))
+ lst))
(defun ring-member (ring item)
"Return index of ITEM if on RING, else nil.
Comparison is done via `equal'. The index is 0-based."
(catch 'found
- (dotimes (ind (ring-length ring) nil)
+ (dotimes (ind (ring-length ring))
(when (equal item (ring-ref ring ind))
(throw 'found ind)))))
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index a2927117342..386232c6eef 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -521,7 +521,7 @@ ARG is optional."
(setq args (nconc (delq ?- args) (list ?-))))
((setq m (assq ?- args))
;; next to the bracket's range, make the second range
- (setcdr args (cons m (delq m args))))))
+ (setcdr args (cons m (delq m (cdr args)))))))
;; bracket in the end range
;; => "[]...-]"
((setq m (rassq ?\] args))
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index d12025cf0c4..23e444fe241 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -4,7 +4,7 @@
;; Author: Nicolas Petton <nicolas@petton.fr>
;; Keywords: sequences
-;; Version: 2.3
+;; Version: 2.20
;; Package: seq
;; Maintainer: emacs-devel@gnu.org
@@ -52,7 +52,7 @@
;; - `seq-copy'
;; - `seq-into'
;;
-;; All functions are tested in test/automated/seq-tests.el
+;; All functions are tested in test/lisp/emacs-lisp/seq-tests.el
;;; Code:
@@ -87,7 +87,7 @@ given, and the match does not fail."
ARGS can also include the `&rest' marker followed by a variable
name to be bound to the rest of SEQUENCE."
- (declare (indent 2) (debug t))
+ (declare (indent 2) (debug (sexp form body)))
`(pcase-let ((,(seq--make-pcase-patterns args) ,sequence))
,@body))
@@ -117,6 +117,16 @@ Return SEQUENCE."
(defalias 'seq-each #'seq-do)
+(defun seq-do-indexed (function sequence)
+ "Apply FUNCTION to each element of SEQUENCE and return nil.
+Unlike `seq-map', FUNCTION takes two arguments: the element of
+the sequence, and its index within the sequence."
+ (let ((index 0))
+ (seq-do (lambda (elt)
+ (funcall function elt index)
+ (setq index (1+ index)))
+ sequence)))
+
(cl-defgeneric seqp (sequence)
"Return non-nil if SEQUENCE is a sequence, nil otherwise."
(sequencep sequence))
@@ -144,6 +154,18 @@ if positive or too small if negative)."
sequence)
(nreverse result)))
+(defun seq-map-indexed (function sequence)
+ "Return the result of applying FUNCTION to each element of SEQUENCE.
+Unlike `seq-map', FUNCTION takes two arguments: the element of
+the sequence, and its index within the sequence."
+ (let ((index 0))
+ (seq-map (lambda (elt)
+ (prog1
+ (funcall function elt index)
+ (setq index (1+ index))))
+ sequence)))
+
+
;; faster implementation for sequences (sequencep)
(cl-defmethod seq-map (function (sequence sequence))
(mapcar function sequence))
@@ -156,7 +178,8 @@ Return a list of the results.
\(fn FUNCTION SEQUENCES...)"
(let ((result nil)
- (sequences (seq-map (lambda (s) (seq-into s 'list))
+ (sequences (seq-map (lambda (s)
+ (seq-into s 'list))
(cons sequence sequences))))
(while (not (memq nil sequences))
(push (apply function (seq-map #'car sequences)) result)
@@ -206,6 +229,16 @@ The result is a sequence of the same type as SEQUENCE."
(cl-defmethod seq-sort (pred (list list))
(sort (seq-copy list) pred))
+(defun seq-sort-by (function pred sequence)
+ "Sort SEQUENCE using PRED as a comparison function.
+Elements of SEQUENCE are transformed by FUNCTION before being
+sorted. FUNCTION must be a function of one argument."
+ (seq-sort (lambda (a b)
+ (funcall pred
+ (funcall function a)
+ (funcall function b)))
+ sequence))
+
(cl-defgeneric seq-reverse (sequence)
"Return a sequence with elements of SEQUENCE in reverse order."
(let ((result '()))
@@ -240,9 +273,9 @@ of sequence."
TYPE can be one of the following symbols: vector, string or
list."
(pcase type
- (`vector (vconcat sequence))
- (`string (concat sequence))
- (`list (append sequence nil))
+ (`vector (seq--into-vector sequence))
+ (`string (seq--into-string sequence))
+ (`list (seq--into-list sequence))
(_ (error "Not a sequence type name: %S" type))))
(cl-defgeneric seq-filter (pred sequence)
@@ -284,7 +317,8 @@ If SEQUENCE is empty, return INITIAL-VALUE and FUNCTION is not called."
t))
(cl-defgeneric seq-some (pred sequence)
- "Return the first value for which if (PRED element) is non-nil for in SEQUENCE."
+ "Return non-nil if PRED is satisfied for at least one element of SEQUENCE.
+If so, return the first non-nil value returned by PRED."
(catch 'seq--break
(seq-doseq (elt sequence)
(let ((result (funcall pred elt)))
@@ -317,9 +351,16 @@ found or not."
"Return the first element in SEQUENCE that is equal to ELT.
Equality is defined by TESTFN if non-nil or by `equal' if nil."
(seq-some (lambda (e)
- (funcall (or testfn #'equal) elt e))
+ (when (funcall (or testfn #'equal) elt e)
+ e))
sequence))
+(cl-defgeneric seq-set-equal-p (sequence1 sequence2 &optional testfn)
+ "Return non-nil if SEQUENCE1 and SEQUENCE2 contain the same elements, regardless of order.
+Equality is defined by TESTFN if non-nil or by `equal' if nil."
+ (and (seq-every-p (lambda (item1) (seq-contains sequence2 item1 testfn)) sequence1)
+ (seq-every-p (lambda (item2) (seq-contains sequence1 item2 testfn)) sequence2)))
+
(cl-defgeneric seq-position (sequence elt &optional testfn)
"Return the index of the first element in SEQUENCE that is equal to ELT.
Equality is defined by TESTFN if non-nil or by `equal' if nil."
@@ -443,16 +484,20 @@ SEQUENCE must be a sequence of numbers or markers."
"Return element of SEQUENCE at the index N.
If no element is found, return nil."
(ignore-errors (seq-elt sequence n)))
+
+(cl-defgeneric seq-random-elt (sequence)
+ "Return a random element from SEQUENCE.
+Signal an error if SEQUENCE is empty."
+ (if (seq-empty-p sequence)
+ (error "Sequence cannot be empty")
+ (seq-elt sequence (random (seq-length sequence)))))
;;; Optimized implementations for lists
(cl-defmethod seq-drop ((list list) n)
"Optimized implementation of `seq-drop' for lists."
- (while (and list (> n 0))
- (setq list (cdr list)
- n (1- n)))
- list)
+ (nthcdr n list))
(cl-defmethod seq-take ((list list) n)
"Optimized implementation of `seq-take' for lists."
@@ -473,6 +518,24 @@ If no element is found, return nil."
(null list))
+(defun seq--into-list (sequence)
+ "Concatenate the elements of SEQUENCE into a list."
+ (if (listp sequence)
+ sequence
+ (append sequence nil)))
+
+(defun seq--into-vector (sequence)
+ "Concatenate the elements of SEQUENCE into a vector."
+ (if (vectorp sequence)
+ sequence
+ (vconcat sequence)))
+
+(defun seq--into-string (sequence)
+ "Concatenate the elements of SEQUENCE into a string."
+ (if (stringp sequence)
+ sequence
+ (concat sequence)))
+
(defun seq--activate-font-lock-keywords ()
"Activate font-lock keywords for some symbols defined in seq."
(font-lock-add-keywords 'emacs-lisp-mode
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index 4d02b751afe..7baccbc7524 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -123,6 +123,8 @@
(eval-when-compile (require 'cl-lib))
+(require 'prog-mode)
+
(defgroup smie nil
"Simple Minded Indentation Engine."
:group 'languages)
@@ -1455,7 +1457,7 @@ in order to figure out the indentation of some other (further down) point."
;; Start the file at column 0.
(save-excursion
(forward-comment (- (point)))
- (if (bobp) 0)))
+ (if (bobp) (prog-first-column))))
(defun smie-indent-close ()
;; Align close paren with opening paren.
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 4548d749fe8..849ac19d6a5 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -30,9 +30,12 @@
;; Do not document these functions in the lispref.
;; http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01006.html
+;; NB If you want to use this library, it's almost always correct to use:
+;; (eval-when-compile (require 'subr-x))
+
;;; Code:
-(require 'pcase)
+(eval-when-compile (require 'cl-lib))
(defmacro internal--thread-argument (first? &rest forms)
@@ -114,14 +117,19 @@ threading."
binding))
bindings)))
-(defmacro if-let (bindings then &rest else)
- "Process BINDINGS and if all values are non-nil eval THEN, else ELSE.
-Argument BINDINGS is a list of tuples whose car is a symbol to be
-bound and (optionally) used in THEN, and its cadr is a sexp to be
-evalled to set symbol's value. In the special case you only want
-to bind a single value, BINDINGS can just be a plain tuple."
+(defmacro if-let* (bindings then &rest else)
+ "Bind variables according to VARLIST and eval THEN or ELSE.
+Each binding is evaluated in turn with `let*', and evaluation
+stops if a binding value is nil. If all are non-nil, the value
+of THEN is returned, or the last form in ELSE is returned.
+Each element of VARLIST is a symbol (which is bound to nil)
+or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).
+In the special case you only want to bind a single value,
+VARLIST can just be a plain tuple.
+\n(fn VARLIST THEN ELSE...)"
(declare (indent 2)
- (debug ([&or (&rest (symbolp form)) (symbolp form)] form body)))
+ (debug ([&or (&rest [&or symbolp (symbolp form)]) (symbolp form)]
+ form body)))
(when (and (<= (length bindings) 2)
(not (listp (car bindings))))
;; Adjust the single binding case
@@ -131,30 +139,34 @@ to bind a single value, BINDINGS can just be a plain tuple."
,then
,@else)))
-(defmacro when-let (bindings &rest body)
- "Process BINDINGS and if all values are non-nil eval BODY.
-Argument BINDINGS is a list of tuples whose car is a symbol to be
-bound and (optionally) used in BODY, and its cadr is a sexp to be
-evalled to set symbol's value. In the special case you only want
-to bind a single value, BINDINGS can just be a plain tuple."
+(defmacro when-let* (bindings &rest body)
+ "Bind variables according to VARLIST and conditionally eval BODY.
+Each binding is evaluated in turn with `let*', and evaluation
+stops if a binding value is nil. If all are non-nil, the value
+of the last form in BODY is returned.
+Each element of VARLIST is a symbol (which is bound to nil)
+or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).
+In the special case you only want to bind a single value,
+VARLIST can just be a plain tuple.
+\n(fn VARLIST BODY...)"
(declare (indent 1) (debug if-let))
(list 'if-let bindings (macroexp-progn body)))
+(defalias 'if-let 'if-let*)
+(defalias 'when-let 'when-let*)
+(defalias 'and-let* 'when-let*)
+
(defsubst hash-table-empty-p (hash-table)
"Check whether HASH-TABLE is empty (has 0 elements)."
(zerop (hash-table-count hash-table)))
(defsubst hash-table-keys (hash-table)
"Return a list of keys in HASH-TABLE."
- (let ((keys '()))
- (maphash (lambda (k _v) (push k keys)) hash-table)
- keys))
+ (cl-loop for k being the hash-keys of hash-table collect k))
(defsubst hash-table-values (hash-table)
"Return a list of values in HASH-TABLE."
- (let ((values '()))
- (maphash (lambda (_k v) (push v values)) hash-table)
- values))
+ (cl-loop for v being the hash-values of hash-table collect v))
(defsubst string-empty-p (string)
"Check whether STRING is empty."
@@ -166,21 +178,27 @@ to bind a single value, BINDINGS can just be a plain tuple."
(define-obsolete-function-alias 'string-reverse 'reverse "25.1")
-(defsubst string-trim-left (string)
- "Remove leading whitespace from STRING."
- (if (string-match "\\`[ \t\n\r]+" string)
+(defsubst string-trim-left (string &optional regexp)
+ "Trim STRING of leading string matching REGEXP.
+
+REGEXP defaults to \"[ \\t\\n\\r]+\"."
+ (if (string-match (concat "\\`\\(?:" (or regexp "[ \t\n\r]+")"\\)") string)
(replace-match "" t t string)
string))
-(defsubst string-trim-right (string)
- "Remove trailing whitespace from STRING."
- (if (string-match "[ \t\n\r]+\\'" string)
+(defsubst string-trim-right (string &optional regexp)
+ "Trim STRING of trailing string matching REGEXP.
+
+REGEXP defaults to \"[ \\t\\n\\r]+\"."
+ (if (string-match (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") string)
(replace-match "" t t string)
string))
-(defsubst string-trim (string)
- "Remove leading and trailing whitespace from STRING."
- (string-trim-left (string-trim-right string)))
+(defsubst string-trim (string &optional trim-left trim-right)
+ "Trim STRING of leading and trailing strings matching TRIM-LEFT and TRIM-RIGHT.
+
+TRIM-LEFT and TRIM-RIGHT default to \"[ \\t\\n\\r]+\"."
+ (string-trim-left (string-trim-right string trim-right) trim-left))
(defsubst string-blank-p (string)
"Check whether STRING is either empty or only whitespace."
@@ -198,6 +216,176 @@ to bind a single value, BINDINGS can just be a plain tuple."
(substring string 0 (- (length string) (length suffix)))
string))
+(defun read-multiple-choice (prompt choices)
+ "Ask user a multiple choice question.
+PROMPT should be a string that will be displayed as the prompt.
+
+CHOICES is an alist where the first element in each entry is a
+character to be entered, the second element is a short name for
+the entry to be displayed while prompting (if there's room, it
+might be shortened), and the third, optional entry is a longer
+explanation that will be displayed in a help buffer if the user
+requests more help.
+
+This function translates user input into responses by consulting
+the bindings in `query-replace-map'; see the documentation of
+that variable for more information. In this case, the useful
+bindings are `recenter', `scroll-up', and `scroll-down'. If the
+user enters `recenter', `scroll-up', or `scroll-down' responses,
+perform the requested window recentering or scrolling and ask
+again.
+
+When `use-dialog-box' is t (the default), this function can pop
+up a dialog window to collect the user input. That functionality
+requires `display-popup-menus-p' to return t. Otherwise, a text
+dialog will be used.
+
+The return value is the matching entry from the CHOICES list.
+
+Usage example:
+
+\(read-multiple-choice \"Continue connecting?\"
+ \\='((?a \"always\")
+ (?s \"session only\")
+ (?n \"no\")))"
+ (let* ((altered-names nil)
+ (full-prompt
+ (format
+ "%s (%s): "
+ prompt
+ (mapconcat
+ (lambda (elem)
+ (let* ((name (cadr elem))
+ (pos (seq-position name (car elem)))
+ (altered-name
+ (cond
+ ;; Not in the name string.
+ ((not pos)
+ (format "[%c] %s" (car elem) name))
+ ;; The prompt character is in the name, so highlight
+ ;; it on graphical terminals...
+ ((display-supports-face-attributes-p
+ '(:underline t) (window-frame))
+ (setq name (copy-sequence name))
+ (put-text-property pos (1+ pos)
+ 'face 'read-multiple-choice-face
+ name)
+ name)
+ ;; And put it in [bracket] on non-graphical terminals.
+ (t
+ (concat
+ (substring name 0 pos)
+ "["
+ (upcase (substring name pos (1+ pos)))
+ "]"
+ (substring name (1+ pos)))))))
+ (push (cons (car elem) altered-name)
+ altered-names)
+ altered-name))
+ (append choices '((?? "?")))
+ ", ")))
+ tchar buf wrong-char answer)
+ (save-window-excursion
+ (save-excursion
+ (while (not tchar)
+ (message "%s%s"
+ (if wrong-char
+ "Invalid choice. "
+ "")
+ full-prompt)
+ (setq tchar
+ (if (and (display-popup-menus-p)
+ last-input-event ; not during startup
+ (listp last-nonmenu-event)
+ use-dialog-box)
+ (x-popup-dialog
+ t
+ (cons prompt
+ (mapcar
+ (lambda (elem)
+ (cons (capitalize (cadr elem))
+ (car elem)))
+ choices)))
+ (condition-case nil
+ (let ((cursor-in-echo-area t))
+ (read-char))
+ (error nil))))
+ (setq answer (lookup-key query-replace-map (vector tchar) t))
+ (setq tchar
+ (cond
+ ((eq answer 'recenter)
+ (recenter) t)
+ ((eq answer 'scroll-up)
+ (ignore-errors (scroll-up-command)) t)
+ ((eq answer 'scroll-down)
+ (ignore-errors (scroll-down-command)) t)
+ ((eq answer 'scroll-other-window)
+ (ignore-errors (scroll-other-window)) t)
+ ((eq answer 'scroll-other-window-down)
+ (ignore-errors (scroll-other-window-down)) t)
+ (t tchar)))
+ (when (eq tchar t)
+ (setq wrong-char nil
+ tchar nil))
+ ;; The user has entered an invalid choice, so display the
+ ;; help messages.
+ (when (and (not (eq tchar nil))
+ (not (assq tchar choices)))
+ (setq wrong-char (not (memq tchar '(?? ?\C-h)))
+ tchar nil)
+ (when wrong-char
+ (ding))
+ (with-help-window (setq buf (get-buffer-create
+ "*Multiple Choice Help*"))
+ (with-current-buffer buf
+ (erase-buffer)
+ (pop-to-buffer buf)
+ (insert prompt "\n\n")
+ (let* ((columns (/ (window-width) 25))
+ (fill-column 21)
+ (times 0)
+ (start (point)))
+ (dolist (elem choices)
+ (goto-char start)
+ (unless (zerop times)
+ (if (zerop (mod times columns))
+ ;; Go to the next "line".
+ (goto-char (setq start (point-max)))
+ ;; Add padding.
+ (while (not (eobp))
+ (end-of-line)
+ (insert (make-string (max (- (* (mod times columns)
+ (+ fill-column 4))
+ (current-column))
+ 0)
+ ?\s))
+ (forward-line 1))))
+ (setq times (1+ times))
+ (let ((text
+ (with-temp-buffer
+ (insert (format
+ "%c: %s\n"
+ (car elem)
+ (cdr (assq (car elem) altered-names))))
+ (fill-region (point-min) (point-max))
+ (when (nth 2 elem)
+ (let ((start (point)))
+ (insert (nth 2 elem))
+ (unless (bolp)
+ (insert "\n"))
+ (fill-region start (point-max))))
+ (buffer-string))))
+ (goto-char start)
+ (dolist (line (split-string text "\n"))
+ (end-of-line)
+ (if (bolp)
+ (insert line "\n")
+ (insert line))
+ (forward-line 1)))))))))))
+ (when (buffer-live-p buf)
+ (kill-buffer buf))
+ (assq tchar choices)))
+
(provide 'subr-x)
;;; subr-x.el ends here
diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el
index 58158150b08..d1d5176944c 100644
--- a/lisp/emacs-lisp/syntax.el
+++ b/lisp/emacs-lisp/syntax.el
@@ -316,6 +316,9 @@ END) suitable for `syntax-propertize-function'."
(unless (eq funs
(cdr syntax-propertize-extend-region-functions))
(setq funs syntax-propertize-extend-region-functions)))))
+ ;; Flush ppss cache between the original value of `start' and that
+ ;; set above by syntax-propertize-extend-region-functions.
+ (syntax-ppss-flush-cache start)
;; Move the limit before calling the function, so the function
;; can use syntax-ppss.
(setq syntax-propertize--done end)
@@ -417,6 +420,9 @@ point (where the PPSS is equivalent to nil).")
(error nil)))
syntax-ppss-stats))
+(defvar-local syntax-ppss-table nil
+ "Syntax-table to use during `syntax-ppss', if any.")
+
(defun syntax-ppss (&optional pos)
"Parse-Partial-Sexp State at POS, defaulting to point.
The returned value is the same as that of `parse-partial-sexp'
@@ -432,6 +438,7 @@ running the hook."
(unless pos (setq pos (point)))
(syntax-propertize pos)
;;
+ (with-syntax-table (or syntax-ppss-table (syntax-table))
(let ((old-ppss (cdr syntax-ppss-last))
(old-pos (car syntax-ppss-last))
(ppss nil)
@@ -568,7 +575,7 @@ running the hook."
;; we may end up calling parse-partial-sexp with a position before
;; point-min. In that case, just parse from point-min assuming
;; a nil state.
- (parse-partial-sexp (point-min) pos)))))
+ (parse-partial-sexp (point-min) pos))))))
;; Debugging functions
diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el
index 415c22553df..b6b49b1bfa2 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -102,6 +102,8 @@ It is called with two arguments, ID and COLS. ID is a Lisp
object identifying the entry, and COLS is a vector of column
descriptors, as documented in `tabulated-list-entries'.")
+(defvar tabulated-list--near-rows)
+
(defvar-local tabulated-list-sort-key nil
"Sort key for the current Tabulated List mode buffer.
If nil, no additional sorting is performed.
@@ -257,6 +259,12 @@ Do nothing if `tabulated-list--header-string' is nil."
(make-overlay (point-min) (point))))
(overlay-put tabulated-list--header-overlay 'face 'underline))))
+(defsubst tabulated-list-header-overlay-p (&optional pos)
+ "Return non-nil if there is a fake header.
+Optional arg POS is a buffer position where to look for a fake header;
+defaults to `point-min'."
+ (overlays-at (or pos (point-min))))
+
(defun tabulated-list-revert (&rest ignored)
"The `revert-buffer-function' for `tabulated-list-mode'.
It runs `tabulated-list-revert-hook', then calls `tabulated-list-print'."
@@ -298,6 +306,14 @@ column. Negate the predicate that would be returned if
(lambda (a b) (not (funcall sorter a b)))
sorter))))
+(defsubst tabulated-list--col-local-max-widths (col)
+ "Return maximum entry widths at column COL around current row.
+Check the current row, the previous one and the next row."
+ (apply #'max (mapcar (lambda (x)
+ (let ((nt (elt x col)))
+ (string-width (if (stringp nt) nt (car nt)))))
+ tabulated-list--near-rows)))
+
(defun tabulated-list-print (&optional remember-pos update)
"Populate the current Tabulated List mode buffer.
This sorts the `tabulated-list-entries' list if sorting is
@@ -340,8 +356,14 @@ changing `tabulated-list-sort-key'."
(unless tabulated-list-use-header-line
(tabulated-list-print-fake-header)))
;; Finally, print the resulting list.
- (dolist (elt entries)
- (let ((id (car elt)))
+ (while entries
+ (let* ((elt (car entries))
+ (tabulated-list--near-rows
+ (list
+ (or (tabulated-list-get-entry (point-at-bol 0)) (cadr elt))
+ (cadr elt)
+ (or (cadr (cadr entries)) (cadr elt))))
+ (id (car elt)))
(and entry-id
(equal entry-id id)
(setq entry-id nil
@@ -368,7 +390,8 @@ changing `tabulated-list-sort-key'."
(t t)))
(let ((old (point)))
(forward-line 1)
- (delete-region old (point)))))))
+ (delete-region old (point))))))
+ (setq entries (cdr entries)))
(set-buffer-modified-p nil)
;; If REMEMBER-POS was specified, move to the "old" location.
(if saved-pt
@@ -389,8 +412,13 @@ of column descriptors."
(inhibit-read-only t))
(if (> tabulated-list-padding 0)
(insert (make-string x ?\s)))
- (dotimes (n ncols)
- (setq x (tabulated-list-print-col n (aref cols n) x)))
+ (let ((tabulated-list--near-rows ; Bind it if not bound yet (Bug#25506).
+ (or (bound-and-true-p tabulated-list--near-rows)
+ (list (or (tabulated-list-get-entry (point-at-bol 0))
+ cols)
+ cols))))
+ (dotimes (n ncols)
+ (setq x (tabulated-list-print-col n (aref cols n) x))))
(insert ?\n)
;; Ever so slightly faster than calling `put-text-property' twice.
(add-text-properties
@@ -402,8 +430,6 @@ of column descriptors."
N is the column number, COL-DESC is a column descriptor (see
`tabulated-list-entries'), and X is the column number at point.
Return the column number after insertion."
- ;; TODO: don't truncate to `width' if the next column is align-right
- ;; and has some space left.
(let* ((format (aref tabulated-list-format n))
(name (nth 0 format))
(width (nth 1 format))
@@ -414,12 +440,29 @@ Return the column number after insertion."
(label-width (string-width label))
(help-echo (concat (car format) ": " label))
(opoint (point))
- (not-last-col (< (1+ n) (length tabulated-list-format))))
+ (not-last-col (< (1+ n) (length tabulated-list-format)))
+ available-space)
+ (when not-last-col
+ (let* ((next-col-format (aref tabulated-list-format (1+ n)))
+ (next-col-right-align (plist-get (nthcdr 3 next-col-format)
+ :right-align))
+ (next-col-width (nth 1 next-col-format)))
+ (setq available-space
+ (if (and (not right-align)
+ next-col-right-align)
+ (-
+ (+ width next-col-width)
+ (min next-col-width
+ (tabulated-list--col-local-max-widths (1+ n))))
+ width))))
;; Truncate labels if necessary (except last column).
- (and not-last-col
- (> label-width width)
- (setq label (truncate-string-to-width label width nil nil t)
- label-width width))
+ ;; Don't truncate to `width' if the next column is align-right
+ ;; and has some space left, truncate to `available-space' instead.
+ (when (and not-last-col
+ (> label-width available-space)
+ (setq label (truncate-string-to-width
+ label available-space nil nil t)
+ label-width available-space)))
(setq label (bidi-string-mark-left-to-right label))
(when (and right-align (> width label-width))
(let ((shift (- width label-width)))
@@ -437,7 +480,7 @@ Return the column number after insertion."
(when not-last-col
(when (> pad-right 0) (insert (make-string pad-right ?\s)))
(insert (propertize
- (make-string (- next-x x label-width pad-right) ?\s)
+ (make-string (- width (min width label-width)) ?\s)
'display `(space :align-to ,next-x))))
(put-text-property opoint (point) 'tabulated-list-column-name name)
next-x)))
@@ -494,7 +537,12 @@ this is the vector stored within it."
(when (< pos eol)
(delete-region pos (next-single-property-change pos prop nil eol))
(goto-char pos)
- (tabulated-list-print-col col desc (current-column))
+ (let ((tabulated-list--near-rows
+ (list
+ (tabulated-list-get-entry (point-at-bol 0))
+ entry
+ (or (tabulated-list-get-entry (point-at-bol 2)) entry))))
+ (tabulated-list-print-col col desc (current-column)))
(if change-entry-data
(aset entry col desc))
(put-text-property pos (point) 'tabulated-list-id id)
diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el
index 34dc4b8d6b3..433ad38a147 100644
--- a/lisp/emacs-lisp/testcover.el
+++ b/lisp/emacs-lisp/testcover.el
@@ -184,6 +184,7 @@ call to one of the `testcover-1value-functions'."
;;; Add instrumentation to your module
;;;=========================================================================
+;;;###autoload
(defun testcover-start (filename &optional byte-compile)
"Uses edebug to instrument all macros and functions in FILENAME, then
changes the instrumentation from edebug to testcover--much faster, no
diff --git a/lisp/emacs-lisp/thunk.el b/lisp/emacs-lisp/thunk.el
index be0a90fefde..f4c075d22ce 100644
--- a/lisp/emacs-lisp/thunk.el
+++ b/lisp/emacs-lisp/thunk.el
@@ -42,8 +42,6 @@
;;
;; (thunk-force delayed)
-;; Tests are located at test/automated/thunk-tests.el
-
;;; Code:
(defmacro thunk-delay (&rest body)
diff --git a/lisp/emacs-lisp/timer-list.el b/lisp/emacs-lisp/timer-list.el
new file mode 100644
index 00000000000..1a38254bcba
--- /dev/null
+++ b/lisp/emacs-lisp/timer-list.el
@@ -0,0 +1,112 @@
+;;; timer-list.el --- list active timers in a buffer
+
+;; Copyright (C) 2016-2017 Free Software Foundation, Inc.
+
+;; Maintainer: emacs-devel@gnu.org
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+;;;###autoload
+(defun timer-list (&optional _ignore-auto _nonconfirm)
+ "List all timers in a buffer."
+ (interactive)
+ (pop-to-buffer-same-window (get-buffer-create "*timer-list*"))
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (timer-list-mode)
+ (dolist (timer (append timer-list timer-idle-list))
+ (insert (format "%4s %10s %8s %s"
+ ;; Idle.
+ (if (aref timer 7)
+ "*"
+ " ")
+ ;; Next time.
+ (let ((time (float-time (list (aref timer 1)
+ (aref timer 2)
+ (aref timer 3)))))
+ (format "%.2f"
+ (if (aref timer 7)
+ time
+ (- (float-time (list (aref timer 1)
+ (aref timer 2)
+ (aref timer 3)))
+ (float-time)))))
+ ;; Repeat.
+ (let ((repeat (aref timer 4)))
+ (cond
+ ((numberp repeat)
+ (format "%.2f" (/ repeat 60)))
+ ((null repeat)
+ "-")
+ (t
+ (format "%s" repeat))))
+ ;; Function.
+ (let ((function (aref timer 5)))
+ (replace-regexp-in-string
+ "\n" " "
+ (cond
+ ((byte-code-function-p function)
+ (replace-regexp-in-string
+ "[^-A-Za-z0-9 ]" ""
+ (format "%s" function)))
+ (t
+ (format "%s" function)))))))
+ (put-text-property (line-beginning-position)
+ (1+ (line-beginning-position))
+ 'timer timer)
+ (insert "\n")))
+ (goto-char (point-min)))
+;; This command can be destructive if they don't know what they are
+;; doing. Kids, don't try this at home!
+;;;###autoload (put 'timer-list 'disabled "Beware: manually canceling timers can ruin your Emacs session.")
+
+(defvar timer-list-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "c" 'timer-list-cancel)
+ (easy-menu-define nil map ""
+ '("Timers"
+ ["Cancel" timer-list-cancel t]))
+ map))
+
+(define-derived-mode timer-list-mode special-mode "timer-list"
+ "Mode for listing and controlling timers."
+ (setq truncate-lines t)
+ (buffer-disable-undo)
+ (setq-local revert-buffer-function 'timer-list)
+ (setq buffer-read-only t)
+ (setq header-line-format
+ (format "%4s %10s %8s %s"
+ "Idle" "Next" "Repeat" "Function")))
+
+(defun timer-list-cancel ()
+ "Cancel the timer on the line under point."
+ (interactive)
+ (let ((timer (get-text-property (line-beginning-position) 'timer))
+ (inhibit-read-only t))
+ (unless timer
+ (error "No timer on the current line"))
+ (cancel-timer timer)
+ (delete-region (line-beginning-position)
+ (line-beginning-position 2))))
+
+(provide 'timer-list)
+
+;;; timer-list.el ends here
diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el
index ba87543f5b0..d872256dad4 100644
--- a/lisp/emacs-lisp/timer.el
+++ b/lisp/emacs-lisp/timer.el
@@ -1,4 +1,4 @@
-;;; timer.el --- run a function with args at some time in future
+;;; timer.el --- run a function with args at some time in future -*- lexical-binding: t -*-
;; Copyright (C) 1996, 2001-2017 Free Software Foundation, Inc.