summaryrefslogtreecommitdiff
path: root/lisp/subr.el
diff options
context:
space:
mode:
authorGregory Heytings <gregory@heytings.org>2022-10-30 17:00:35 +0100
committerGregory Heytings <gregory@heytings.org>2022-10-30 17:00:35 +0100
commitaef803d6c3d61004f15d0bc82fa7bf9952302312 (patch)
tree087c444f788cda27006ddc066ad430f62f5ac02a /lisp/subr.el
parent3bf19c417fd39766ee9c7a793c9faadd3bd88478 (diff)
parent3fa4cca3d244f51e471e7779c934278731fc21e9 (diff)
downloademacs-aef803d6c3d61004f15d0bc82fa7bf9952302312.tar.gz
emacs-aef803d6c3d61004f15d0bc82fa7bf9952302312.tar.bz2
emacs-aef803d6c3d61004f15d0bc82fa7bf9952302312.zip
Merge master into feature/improved-locked-narrowing.
Diffstat (limited to 'lisp/subr.el')
-rw-r--r--lisp/subr.el322
1 files changed, 180 insertions, 142 deletions
diff --git a/lisp/subr.el b/lisp/subr.el
index 16eb84caa65..86a3b7ae998 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -311,29 +311,13 @@ Then evaluate RESULT to get return value, default nil.
(signal 'wrong-type-argument (list 'consp spec)))
(unless (<= 2 (length spec) 3)
(signal 'wrong-number-of-arguments (list '(2 . 3) (length spec))))
- ;; It would be cleaner to create an uninterned symbol,
- ;; but that uses a lot more space when many functions in many files
- ;; use dolist.
- ;; FIXME: This cost disappears in byte-compiled lexical-binding files.
- (let ((temp '--dolist-tail--))
- ;; This test does not matter much because both semantics are acceptable,
- ;; but one is slightly faster with dynamic scoping and the other is
- ;; slightly faster (and has cleaner semantics) with lexical scoping.
- (if lexical-binding
- `(let ((,temp ,(nth 1 spec)))
- (while ,temp
- (let ((,(car spec) (car ,temp)))
- ,@body
- (setq ,temp (cdr ,temp))))
- ,@(cdr (cdr spec)))
- `(let ((,temp ,(nth 1 spec))
- ,(car spec))
- (while ,temp
- (setq ,(car spec) (car ,temp))
+ (let ((tail (make-symbol "tail")))
+ `(let ((,tail ,(nth 1 spec)))
+ (while ,tail
+ (let ((,(car spec) (car ,tail)))
,@body
- (setq ,temp (cdr ,temp)))
- ,@(if (cdr (cdr spec))
- `((setq ,(car spec) nil) ,@(cdr (cdr spec))))))))
+ (setq ,tail (cdr ,tail))))
+ ,@(cdr (cdr spec)))))
(defmacro dotimes (spec &rest body)
"Loop a certain number of times.
@@ -346,35 +330,21 @@ in compilation warnings about unused variables.
\(fn (VAR COUNT [RESULT]) BODY...)"
(declare (indent 1) (debug dolist))
- ;; It would be cleaner to create an uninterned symbol,
- ;; but that uses a lot more space when many functions in many files
- ;; use dotimes.
- ;; FIXME: This cost disappears in byte-compiled lexical-binding files.
- (let ((temp '--dotimes-limit--)
- (start 0)
- (end (nth 1 spec)))
- ;; This test does not matter much because both semantics are acceptable,
- ;; but one is slightly faster with dynamic scoping and the other has
- ;; cleaner semantics.
- (if lexical-binding
- (let ((counter '--dotimes-counter--))
- `(let ((,temp ,end)
- (,counter ,start))
- (while (< ,counter ,temp)
- (let ((,(car spec) ,counter))
- ,@body)
- (setq ,counter (1+ ,counter)))
- ,@(if (cddr spec)
- ;; FIXME: This let often leads to "unused var" warnings.
- `((let ((,(car spec) ,counter)) ,@(cddr spec))))))
- `(let ((,temp ,end)
- (,(car spec) ,start))
- (while (< ,(car spec) ,temp)
- ,@body
- (setq ,(car spec) (1+ ,(car spec))))
- ,@(cdr (cdr spec))))))
+ (let ((var (nth 0 spec))
+ (end (nth 1 spec))
+ (upper-bound (make-symbol "upper-bound"))
+ (counter (make-symbol "counter")))
+ `(let ((,upper-bound ,end)
+ (,counter 0))
+ (while (< ,counter ,upper-bound)
+ (let ((,var ,counter))
+ ,@body)
+ (setq ,counter (1+ ,counter)))
+ ,@(if (cddr spec)
+ ;; FIXME: This let often leads to "unused var" warnings.
+ `((let ((,var ,counter)) ,@(cddr spec)))))))
-(defmacro declare (&rest _specs)
+(defmacro declare (&rest specs)
"Do not evaluate any arguments, and return nil.
If a `declare' form appears as the first form in the body of a
`defun' or `defmacro' form, SPECS specifies various additional
@@ -385,8 +355,16 @@ The possible values of SPECS are specified by
`defun-declarations-alist' and `macro-declarations-alist'.
For more information, see info node `(elisp)Declare Form'."
- ;; FIXME: edebug spec should pay attention to defun-declarations-alist.
- nil)
+ ;; `declare' is handled directly by `defun/defmacro' rather than here.
+ ;; If we get here, it's because there's a `declare' somewhere not attached
+ ;; to a `defun/defmacro', i.e. a `declare' which doesn't do what it's
+ ;; intended to do.
+ (let ((form `(declare . ,specs))) ;; FIXME: WIBNI we had &whole?
+ (macroexp-warn-and-return
+ (format-message "Stray `declare' form: %S" form)
+ ;; Make a "unique" harmless form to circumvent
+ ;; the cache in `macroexp-warn-and-return'.
+ `(progn ',form nil) nil 'compile-only)))
(defmacro ignore-errors (&rest body)
"Execute BODY; if an error occurs, return nil.
@@ -1239,7 +1217,7 @@ Subkeymaps may be modified but are not canonicalized."
(defun keyboard-translate (from to)
"Translate character FROM to TO on the current terminal.
-This is a legacy function; see `keymap-translate' for the
+This is a legacy function; see `key-translate' for the
recommended function to use instead.
This function creates a `keyboard-translate-table' if necessary
@@ -1318,7 +1296,7 @@ KEY is a string or vector representing a sequence of keystrokes."
(defun local-key-binding (keys &optional accept-default)
"Return the binding for command KEYS in current local keymap only.
-This is a legacy function; see `keymap-local-binding' for the
+This is a legacy function; see `keymap-local-lookup' for the
recommended function to use instead.
KEYS is a string or vector, a sequence of keystrokes.
@@ -1332,7 +1310,7 @@ about this."
(defun global-key-binding (keys &optional accept-default)
"Return the binding for command KEYS in current global keymap only.
-This is a legacy function; see `keymap-global-binding' for the
+This is a legacy function; see `keymap-global-lookup' for the
recommended function to use instead.
KEYS is a string or vector, a sequence of keystrokes.
@@ -1592,6 +1570,21 @@ in the current Emacs session, then this function may return nil."
;; is this really correct? maybe remove mouse-movement?
(memq (event-basic-type object) '(mouse-1 mouse-2 mouse-3 mouse-movement)))
+(defun event--posn-at-point ()
+ ;; Use `window-point' for the case when the current buffer
+ ;; is temporarily switched to some other buffer (bug#50256)
+ (let* ((pos (window-point))
+ (posn (posn-at-point pos)))
+ (if (null posn) ;; `pos' is "out of sight".
+ (list (selected-window) pos '(0 . 0) 0)
+ ;; If `pos' is inside a chunk of text hidden by an `invisible'
+ ;; or `display' property, `posn-at-point' returns the position
+ ;; that *is* visible, whereas `event--posn-at-point' is used
+ ;; when we have a keyboard event, whose position is `point' even
+ ;; if that position is invisible.
+ (setf (nth 5 posn) pos)
+ posn)))
+
(defun event-start (event)
"Return the starting position of EVENT.
EVENT should be a mouse click, drag, or key press event. If
@@ -1618,10 +1611,7 @@ nil or (STRING . POSITION)'.
For more information, see Info node `(elisp)Click Events'."
(or (and (consp event) (nth 1 event))
- ;; Use `window-point' for the case when the current buffer
- ;; is temporarily switched to some other buffer (bug#50256)
- (posn-at-point (window-point))
- (list (selected-window) (window-point) '(0 . 0) 0)))
+ (event--posn-at-point)))
(defun event-end (event)
"Return the ending position of EVENT.
@@ -1629,10 +1619,7 @@ EVENT should be a click, drag, or key press event.
See `event-start' for a description of the value returned."
(or (and (consp event) (nth (if (consp (nth 2 event)) 2 1) event))
- ;; Use `window-point' for the case when the current buffer
- ;; is temporarily switched to some other buffer (bug#50256)
- (posn-at-point (window-point))
- (list (selected-window) (window-point) '(0 . 0) 0)))
+ (event--posn-at-point)))
(defsubst event-click-count (event)
"Return the multi-click count of EVENT, a click or drag event.
@@ -1824,8 +1811,6 @@ be a list of the form returned by `event-start' and `event-end'."
;;;; Obsolescent names for functions.
-(make-obsolete 'buffer-has-markers-at nil "24.3")
-
(make-obsolete 'invocation-directory "use the variable of the same name."
"27.1")
(make-obsolete 'invocation-name "use the variable of the same name." "27.1")
@@ -1860,7 +1845,12 @@ be a list of the form returned by `event-start' and `event-end'."
(set-advertised-calling-convention 'time-convert '(time form) "29.1")
;;;; Obsolescence declarations for variables, and aliases.
-
+(make-obsolete-variable
+ 'inhibit-point-motion-hooks
+ "use `cursor-intangible-mode' or `cursor-sensor-mode' instead"
+ ;; It's been announced as obsolete in NEWS and in the docstring since Emacs-25,
+ ;; but it's only been marked for compilation warnings since Emacs-29.
+ "25.1")
(make-obsolete-variable 'redisplay-dont-pause nil "24.5")
(make-obsolete-variable 'operating-system-release nil "28.1")
(make-obsolete-variable 'inhibit-changing-match-data 'save-match-data "29.1")
@@ -1891,6 +1881,17 @@ be a list of the form returned by `event-start' and `event-end'."
;; in warnings when using `values' in let-bindings.
;;(make-obsolete-variable 'values "no longer used" "28.1")
+(defvar max-specpdl-size 2500
+ "Former limit on specbindings, now without effect.
+This variable used to limit the size of the specpdl stack which,
+among other things, holds dynamic variable bindings and `unwind-protect'
+activations. To prevent runaway recursion, use `max-lisp-eval-depth'
+instead; it will indirectly limit the specpdl stack size as well.")
+(make-obsolete-variable 'max-specpdl-size nil "29.1")
+
+(make-obsolete-variable 'native-comp-deferred-compilation
+ 'inhibit-automatic-native-compilation "29.1")
+
;;;; Alternate names for functions - these are not being phased out.
@@ -1913,8 +1914,10 @@ be a list of the form returned by `event-start' and `event-end'."
(defalias 'mkdir #'make-directory)
;; These were the XEmacs names, now obsolete:
-(define-obsolete-function-alias 'point-at-eol #'line-end-position "29.1")
-(define-obsolete-function-alias 'point-at-bol #'line-beginning-position "29.1")
+(defalias 'point-at-eol #'line-end-position)
+(make-obsolete 'point-at-eol "use `line-end-position' or `pos-eol' instead." "29.1")
+(defalias 'point-at-bol #'line-beginning-position)
+(make-obsolete 'point-at-bol "use `line-beginning-position' or `pos-bol' instead." "29.1")
(define-obsolete-function-alias 'user-original-login-name #'user-login-name "28.1")
;; These are in obsolete/autoload.el, but are commonly used by
@@ -2527,7 +2530,20 @@ The variable list SPEC is the same as in `if-let'."
(declare (indent 1) (debug if-let))
(list 'if-let spec (macroexp-progn body)))
+(defmacro while-let (spec &rest body)
+ "Bind variables according to SPEC and conditionally evaluate BODY.
+Evaluate each binding in turn, stopping if a binding value is nil.
+If all bindings are non-nil, eval BODY and repeat.
+The variable list SPEC is the same as in `if-let'."
+ (declare (indent 1) (debug if-let))
+ (let ((done (gensym "done")))
+ `(catch ',done
+ (while t
+ (if-let* ,spec
+ (progn
+ ,@body)
+ (throw ',done nil))))))
;; PUBLIC: find if the current mode derives from another.
@@ -3254,7 +3270,14 @@ An obsolete, but still supported form is
where the optional arg MILLISECONDS specifies an additional wait period,
in milliseconds; this was useful when Emacs was built without
floating point support."
- (declare (advertised-calling-convention (seconds &optional nodisp) "22.1"))
+ (declare (advertised-calling-convention (seconds &optional nodisp) "22.1")
+ (compiler-macro
+ (lambda (form)
+ (if (not (or (numberp nodisp) obsolete)) form
+ (macroexp-warn-and-return
+ "Obsolete calling convention for 'sit-for'"
+ `(,(car form) (+ ,seconds (/ (or ,nodisp 0) 1000.0)) ,obsolete)
+ '(obsolete sit-for))))))
;; This used to be implemented in C until the following discussion:
;; https://lists.gnu.org/r/emacs-devel/2006-07/msg00401.html
;; Then it was moved here using an implementation based on an idle timer,
@@ -3544,11 +3567,12 @@ like) while `y-or-n-p' is running)."
(if (or (zerop l) (eq ?\s (aref prompt (1- l))))
"" " ")
(if dialog ""
- (if help-form
- (format "(y, n or %s) "
- (key-description
- (vector help-char)))
- "(y or n) "))))))
+ (substitute-command-keys
+ (if help-form
+ (format "(\\`y', \\`n' or \\`%s') "
+ (key-description
+ (vector help-char)))
+ "(\\`y' or \\`n') ")))))))
;; Preserve the actual command that eventually called
;; `y-or-n-p' (otherwise `repeat' will be repeating
;; `exit-minibuffer').
@@ -3786,10 +3810,6 @@ This finishes the change group by reverting all of its changes."
;;;; Display-related functions.
-;; For compatibility.
-(define-obsolete-function-alias 'redraw-modeline
- #'force-mode-line-update "24.3")
-
(defun momentary-string-display (string pos &optional exit-char message)
"Momentarily display STRING in the buffer at POS.
Display remains until next event is input.
@@ -4077,6 +4097,13 @@ system's shell."
Otherwise, return nil."
(or (stringp object) (null object)))
+(defun list-of-strings-p (object)
+ "Return t if OBJECT is nil or a list of strings."
+ (declare (pure t) (side-effect-free error-free))
+ (while (and (consp object) (stringp (car object)))
+ (setq object (cdr object)))
+ (null object))
+
(defun booleanp (object)
"Return t if OBJECT is one of the two canonical boolean values: t or nil.
Otherwise, return nil."
@@ -4270,15 +4297,17 @@ Comparisons and replacements are done with fixed case."
(error "End after end of buffer"))
(setq end (point-max)))
(save-excursion
- (let ((matches 0)
- (case-fold-search nil))
- (goto-char start)
- (while (search-forward string end t)
- (delete-region (match-beginning 0) (match-end 0))
- (insert replacement)
- (setq matches (1+ matches)))
- (and (not (zerop matches))
- matches))))
+ (goto-char start)
+ (save-restriction
+ (narrow-to-region start end)
+ (let ((matches 0)
+ (case-fold-search nil))
+ (while (search-forward string nil t)
+ (delete-region (match-beginning 0) (match-end 0))
+ (insert replacement)
+ (setq matches (1+ matches)))
+ (and (not (zerop matches))
+ matches)))))
(defun replace-regexp-in-region (regexp replacement &optional start end)
"Replace REGEXP with REPLACEMENT in the region from START to END.
@@ -4305,14 +4334,16 @@ REPLACEMENT can use the following special elements:
(error "End after end of buffer"))
(setq end (point-max)))
(save-excursion
- (let ((matches 0)
- (case-fold-search nil))
- (goto-char start)
- (while (re-search-forward regexp end t)
- (replace-match replacement t)
- (setq matches (1+ matches)))
- (and (not (zerop matches))
- matches))))
+ (goto-char start)
+ (save-restriction
+ (narrow-to-region start end)
+ (let ((matches 0)
+ (case-fold-search nil))
+ (while (re-search-forward regexp nil t)
+ (replace-match replacement t)
+ (setq matches (1+ matches)))
+ (and (not (zerop matches))
+ matches)))))
(defun yank-handle-font-lock-face-property (face start end)
"If `font-lock-defaults' is nil, apply FACE as a `face' property.
@@ -4870,16 +4901,26 @@ the function `undo--wrap-and-run-primitive-undo'."
(let ((undo--combining-change-calls t))
(if (not inhibit-modification-hooks)
(run-hook-with-args 'before-change-functions beg end))
- (let (;; (inhibit-modification-hooks t)
- (before-change-functions
- ;; Ugly Hack: if the body uses syntax-ppss/syntax-propertize
- ;; (e.g. via a regexp-search or sexp-movement triggering
- ;; on-the-fly syntax-propertize), make sure that this gets
- ;; properly refreshed after subsequent changes.
- (if (memq #'syntax-ppss-flush-cache before-change-functions)
- '(syntax-ppss-flush-cache)))
- after-change-functions)
- (setq result (funcall body)))
+ (let ((bcf before-change-functions)
+ (acf after-change-functions)
+ (local-bcf (local-variable-p 'before-change-functions))
+ (local-acf (local-variable-p 'after-change-functions)))
+ (unwind-protect
+ ;; FIXME: WIBNI we could just use `inhibit-modification-hooks'?
+ (progn
+ ;; Ugly Hack: if the body uses syntax-ppss/syntax-propertize
+ ;; (e.g. via a regexp-search or sexp-movement triggering
+ ;; on-the-fly syntax-propertize), make sure that this gets
+ ;; properly refreshed after subsequent changes.
+ (setq-local before-change-functions
+ (if (memq #'syntax-ppss-flush-cache bcf)
+ '(syntax-ppss-flush-cache)))
+ (setq-local after-change-functions nil)
+ (setq result (funcall body)))
+ (if local-bcf (setq before-change-functions bcf)
+ (kill-local-variable 'before-change-functions))
+ (if local-acf (setq after-change-functions acf)
+ (kill-local-variable 'after-change-functions))))
(when (not (eq buffer-undo-list t))
(let ((ap-elt
(list 'apply
@@ -5001,10 +5042,6 @@ If `default-directory' is already an existing directory, it's not changed."
;;; Matching and match data.
-;; We use save-match-data-internal as the local variable because
-;; that works ok in practice (people should not use that variable elsewhere).
-;; We used to use an uninterned symbol; the compiler handles that properly
-;; now, but it generates slower code.
(defmacro save-match-data (&rest body)
"Execute the BODY forms, restoring the global value of the match data.
The value returned is the value of the last form in BODY.
@@ -5016,13 +5053,12 @@ rather than your caller's match data."
;; because that makes a bootstrapping problem
;; if you need to recompile all the Lisp files using interpreted code.
(declare (indent 0) (debug t))
- (list 'let
- '((save-match-data-internal (match-data)))
- (list 'unwind-protect
- (cons 'progn body)
- ;; It is safe to free (evaporate) markers immediately here,
- ;; as Lisp programs should not copy from save-match-data-internal.
- '(set-match-data save-match-data-internal 'evaporate))))
+ (let ((saved-match-data (make-symbol "saved-match-data")))
+ (list 'let
+ (list (list saved-match-data '(match-data)))
+ (list 'unwind-protect
+ (cons 'progn body)
+ (list 'set-match-data saved-match-data t)))))
(defun match-string (num &optional string)
"Return the string of text matched by the previous search or regexp operation.
@@ -5260,6 +5296,8 @@ Modifies the match data; use `save-match-data' if necessary."
(nreverse list)))
+(defalias 'string-split #'split-string)
+
(defun combine-and-quote-strings (strings &optional separator)
"Concatenate the STRINGS, adding the SEPARATOR (default \" \").
This tries to quote the strings to avoid ambiguity such that
@@ -7007,32 +7045,32 @@ CONDITION is either:
(lambda (conditions)
(catch 'match
(dolist (condition conditions)
- (when (cond
- ((eq condition t))
- ((stringp condition)
- (string-match-p condition (buffer-name buffer)))
- ((functionp condition)
- (if (eq 1 (cdr (func-arity condition)))
- (funcall condition buffer)
- (funcall condition buffer arg)))
- ((eq (car-safe condition) 'major-mode)
- (eq
- (buffer-local-value 'major-mode buffer)
- (cdr condition)))
- ((eq (car-safe condition) 'derived-mode)
- (provided-mode-derived-p
- (buffer-local-value 'major-mode buffer)
- (cdr condition)))
- ((eq (car-safe condition) 'not)
- (not (funcall match (cdr condition))))
- ((eq (car-safe condition) 'or)
- (funcall match (cdr condition)))
- ((eq (car-safe condition) 'and)
- (catch 'fail
- (dolist (c (cdr conditions))
- (unless (funcall match c)
- (throw 'fail nil)))
- t)))
+ (when (pcase condition
+ ('t t)
+ ((pred stringp)
+ (string-match-p condition (buffer-name buffer)))
+ ((pred functionp)
+ (if (eq 1 (cdr (func-arity condition)))
+ (funcall condition buffer)
+ (funcall condition buffer arg)))
+ (`(major-mode . ,mode)
+ (eq
+ (buffer-local-value 'major-mode buffer)
+ mode))
+ (`(derived-mode . ,mode)
+ (provided-mode-derived-p
+ (buffer-local-value 'major-mode buffer)
+ mode))
+ (`(not . ,cond)
+ (not (funcall match cond)))
+ (`(or . ,args)
+ (funcall match args))
+ (`(and . ,args)
+ (catch 'fail
+ (dolist (c args)
+ (unless (funcall match (list c))
+ (throw 'fail nil)))
+ t)))
(throw 'match t)))))))
(funcall match (list condition))))