diff options
author | Gregory Heytings <gregory@heytings.org> | 2022-10-30 17:00:35 +0100 |
---|---|---|
committer | Gregory Heytings <gregory@heytings.org> | 2022-10-30 17:00:35 +0100 |
commit | aef803d6c3d61004f15d0bc82fa7bf9952302312 (patch) | |
tree | 087c444f788cda27006ddc066ad430f62f5ac02a /lisp/subr.el | |
parent | 3bf19c417fd39766ee9c7a793c9faadd3bd88478 (diff) | |
parent | 3fa4cca3d244f51e471e7779c934278731fc21e9 (diff) | |
download | emacs-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.el | 322 |
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)))) |