aboutsummaryrefslogtreecommitdiff
path: root/.config/emacs/lisp/libs/compat-31.el
diff options
context:
space:
mode:
Diffstat (limited to '.config/emacs/lisp/libs/compat-31.el')
-rw-r--r--.config/emacs/lisp/libs/compat-31.el416
1 files changed, 416 insertions, 0 deletions
diff --git a/.config/emacs/lisp/libs/compat-31.el b/.config/emacs/lisp/libs/compat-31.el
new file mode 100644
index 0000000..2b61749
--- /dev/null
+++ b/.config/emacs/lisp/libs/compat-31.el
@@ -0,0 +1,416 @@
+;;; compat-31.el --- Functionality added in Emacs 31 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+;; This program 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.
+
+;; This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Functionality added in Emacs 31, needed by older Emacs versions.
+
+;;; Code:
+
+(eval-when-compile (load "compat-macs.el" nil t t))
+(compat-require compat-30 "30.1")
+
+;; TODO Update to 31.1 as soon as the Emacs emacs-31 branch version bumped
+(compat-version "31.0.50")
+
+;;;; Defined in subr.el
+
+(compat-defun error-type-p (symbol) ;; <compat-tests:error-api>
+ "Return non-nil if SYMBOL is a condition type."
+ (get symbol 'error-conditions))
+
+(compat-defun error-has-type-p (error condition) ;; <compat-tests:error-api>
+ "Return non-nil if ERROR is of type CONDITION (or a subtype of it)."
+ (unless (let ((type (car-safe error)))
+ (and type (symbolp type) (listp (cdr error))
+ (error-type-p type)))
+ (signal 'wrong-type-argument (list error)))
+ (or (eq condition t)
+ (memq condition (get (car error) 'error-conditions))))
+
+(compat-defalias error-type car ;; <compat-tests:error-api>
+ "Return the symbol which represents the type of ERROR.
+\n(fn ERROR)")
+
+(compat-defalias error-slot-value elt ;; <compat-tests:error-api>
+ "Access the SLOT of object ERROR.
+Slots are specified by position, and slot 0 is the error symbol.
+\n(fn ERROR SLOT)")
+
+(compat-defun ensure-proper-list (object) ;; <compat-tests:ensure-proper-list>
+ "Return OBJECT as a list.
+If OBJECT is already a proper list, return OBJECT itself. If it's not a
+proper list, return a one-element list containing OBJECT.
+
+`ensure-list' is usually preferable because that function runs in
+constant time, but this one has to traverse the whole of OBJECT."
+ (declare (side-effect-free error-free))
+ (if (proper-list-p object)
+ object
+ (list object)))
+
+(compat-defun set-local (variable value) ;; <compat-tests:set-local>
+ "Make VARIABLE buffer local and set it to VALUE."
+ (set (make-local-variable variable) value))
+
+(compat-defun take-while (pred list) ;; <compat-tests:take-while>
+ "Return the longest prefix of LIST whose elements satisfy PRED."
+ (let ((r nil))
+ (while (and list (funcall pred (car list)))
+ (push (car list) r)
+ (setq list (cdr list)))
+ (nreverse r)))
+
+(compat-defun drop-while (pred list) ;; <compat-tests:drop-while>
+ "Skip initial elements of LIST satisfying PRED and return the rest."
+ (while (and list (funcall pred (car list)))
+ (setq list (cdr list)))
+ list)
+
+(compat-defun all (pred list) ;; <compat-tests:all>
+ "Non-nil if PRED is true for all elements in LIST."
+ (not (drop-while pred list)))
+
+(compat-defun member-if (pred list) ;; <compat-tests:member-if>
+ "Non-nil if PRED is true for at least one element in LIST.
+Returns the LIST suffix starting at the first element that satisfies PRED,
+or nil if none does."
+ (drop-while (lambda (x) (not (funcall pred x))) list))
+
+(compat-defalias any member-if) ;; <compat-tests:member-if>
+
+(compat-defun hash-table-contains-p (key table) ;; <compat-tests:hash-table-contains-p>
+ "Return non-nil if TABLE has an element with KEY."
+ (declare (side-effect-free t))
+ (let ((missing '#:missing))
+ (not (eq (gethash key table missing) missing))))
+
+(compat-defmacro static-when (condition &rest body) ;; <compat-tests:static-when>
+ "A conditional compilation macro.
+Evaluate CONDITION at macro-expansion time. If it is non-nil,
+expand the macro to evaluate all BODY forms sequentially and return
+the value of the last one, or nil if there are none."
+ (declare (indent 1) (debug t))
+ (if body
+ (if (eval condition lexical-binding)
+ (cons 'progn body)
+ nil)
+ (macroexp-warn-and-return (format-message "`static-when' with empty body")
+ (list 'progn nil nil) '(empty-body static-when) t)))
+
+(compat-defmacro static-unless (condition &rest body) ;; <compat-tests:static-unless>
+ "A conditional compilation macro.
+Evaluate CONDITION at macro-expansion time. If it is nil,
+expand the macro to evaluate all BODY forms sequentially and return
+the value of the last one, or nil if there are none."
+ (declare (indent 1) (debug t))
+ (if body
+ (if (eval condition lexical-binding)
+ nil
+ (cons 'progn body))
+ (macroexp-warn-and-return (format-message "`static-unless' with empty body")
+ (list 'progn nil nil) '(empty-body static-unless) t)))
+
+(compat-defun oddp (integer) ;; <compat-tests:oddp>
+ "Return t if INTEGER is odd."
+ (not (eq (% integer 2) 0)))
+
+(compat-defun evenp (integer) ;; <compat-tests:evenp>
+ "Return t if INTEGER is even."
+ (eq (% integer 2) 0))
+
+(compat-defun plusp (number) ;; <compat-tests:plusp>
+ "Return t if NUMBER is positive."
+ (> number 0))
+
+(compat-defun minusp (number) ;; <compat-tests:minusp>
+ "Return t if NUMBER is negative."
+ (< number 0))
+
+(compat-defmacro incf (place &optional delta) ;; <compat-tests:incf>
+ "Increment PLACE by DELTA (default to 1).
+
+The DELTA is first added to PLACE, and then stored in PLACE.
+Return the incremented value of PLACE.
+
+See also `decf'."
+ (gv-letplace (getter setter) place
+ (funcall setter `(+ ,getter ,(or delta 1)))))
+
+(compat-defmacro decf (place &optional delta) ;; <compat-tests:decf>
+ "Decrement PLACE by DELTA (default to 1).
+
+The DELTA is first subtracted from PLACE, and then stored in PLACE.
+Return the decremented value of PLACE.
+
+See also `incf'."
+ (gv-letplace (getter setter) place
+ (funcall setter `(- ,getter ,(or delta 1)))))
+
+;;;; Defined in color.el
+
+(compat-defun color-blend (a b &optional alpha) ;; <compat-tests:color-blend>
+ "Blend the two colors A and B in linear space with ALPHA.
+A and B should be lists (RED GREEN BLUE), where each element is
+between 0.0 and 1.0, inclusive. ALPHA controls the influence A
+has on the result and should be between 0.0 and 1.0, inclusive.
+
+For instance:
+
+ (color-blend \\='(1 0.5 1) \\='(0 0 0) 0.75)
+ => (0.75 0.375 0.75)"
+ (setq alpha (or alpha 0.5))
+ (let (blend)
+ (dotimes (i 3)
+ (push (+ (* (nth i a) alpha) (* (nth i b) (- 1 alpha))) blend))
+ (nreverse blend)))
+
+;;;; Defined in time-date.el
+
+(compat-defvar seconds-to-string ;; <compat-tests:seconds-to-string>
+ (list (list 1 "ms" 0.001)
+ (list 100 "s" 1)
+ (list (* 60 100) "m" 60.0)
+ (list (* 3600 30) "h" 3600.0)
+ (list (* 3600 24 400) "d" (* 3600.0 24.0))
+ (list nil "y" (* 365.25 24 3600)))
+ "Formatting used by the function `seconds-to-string'.")
+
+(compat-defvar seconds-to-string-readable ;; <compat-tests:seconds-to-string>
+ `(("Y" "year" "years" ,(round (* 60 60 24 365.2425)))
+ ("M" "month" "months" ,(round (* 60 60 24 30.436875)))
+ ("w" "week" "weeks" ,(* 60 60 24 7))
+ ("d" "day" "days" ,(* 60 60 24))
+ ("h" "hour" "hours" ,(* 60 60))
+ ("m" "minute" "minutes" 60)
+ ("s" "second" "seconds" 1))
+ "Formatting used by the function `seconds-to-string' with READABLE set.
+The format is an alist, with string keys ABBREV-UNIT, and elements like:
+
+ (ABBREV-UNIT UNIT UNIT-PLURAL SECS)
+
+where UNIT is a unit of time, ABBREV-UNIT is the abbreviated form of
+UNIT, UNIT-PLURAL is the plural form of UNIT, and SECS is the number of
+seconds per UNIT.")
+
+(compat-defun seconds-to-string (delay &optional readable abbrev precision) ;; <compat-tests:seconds-to-string>
+ "Handle optional arguments READABLE, ABBREV and PRECISION."
+ :extended t
+ (cond
+ ((< delay 0)
+ (concat "-" (seconds-to-string (- delay) readable precision)))
+ (readable
+ (let* ((stsa seconds-to-string-readable)
+ (expanded (eq readable 'expanded))
+ digits
+ (round-to (cond
+ ((wholenump precision)
+ (setq digits precision)
+ (expt 10 (- precision)))
+ ((and (floatp precision) (< precision 1.))
+ (setq digits (- (floor (log precision 10))))
+ precision)
+ (t (setq digits 0) 1)))
+ (dformat (if (> digits 0) (format "%%0.%df" digits)))
+ (padding (if abbrev "" " "))
+ here cnt cnt-pre here-pre cnt-val isfloatp)
+ (if (= (round delay round-to) 0)
+ (format "0%s" (if abbrev "s" " seconds"))
+ (while (and (setq here (pop stsa)) stsa
+ (< (/ delay (nth 3 here)) 1)))
+ (or (and
+ expanded stsa ; smaller unit remains
+ (progn
+ (setq
+ here-pre here here (car stsa)
+ cnt-pre (floor (/ (float delay) (nth 3 here-pre)))
+ cnt (round
+ (/ (- (float delay) (* cnt-pre (nth 3 here-pre)))
+ (nth 3 here))
+ round-to))
+ (if (> cnt 0) t (setq cnt cnt-pre here here-pre here-pre nil))))
+ (setq cnt (round (/ (float delay) (nth 3 here)) round-to)))
+ (setq cnt-val (* cnt round-to)
+ isfloatp (and (> digits 0)
+ (> (- cnt-val (floor cnt-val)) 0.)))
+ (cl-labels
+ ((unit (val here &optional plural)
+ (cond (abbrev (car here))
+ ((and (not plural) (<= (floor val) 1)) (nth 1 here))
+ (t (nth 2 here)))))
+ (concat
+ (when here-pre
+ (concat (number-to-string cnt-pre) padding
+ (unit cnt-pre here-pre) " "))
+ (if isfloatp (format dformat cnt-val)
+ (number-to-string (floor cnt-val)))
+ padding
+ (unit cnt-val here isfloatp)))))) ; float formats are always plural
+ ((= 0 delay) "0s")
+ (t (let ((sts seconds-to-string) here)
+ (while (and (car (setq here (pop sts)))
+ (<= (car here) delay)))
+ (concat (format "%.2f" (/ delay (car (cddr here)))) (cadr here))))))
+
+;;;; Defined in minibuffer.el
+
+(compat-defun completion-list-candidate-at-point (&optional pt) ;; <compat-tests:completion-list-candidate-at-point>
+ "Candidate string and bounds at PT in completions buffer.
+The return value has the format (STR BEG END).
+The optional argument PT defaults to (point)."
+ (let ((pt (or pt (point))) beg end)
+ (cond
+ ((and (/= pt (point-max)) (get-text-property pt 'mouse-face))
+ (setq end pt beg (1+ pt)))
+ ((and (/= pt (point-min)) (get-text-property (1- pt) 'mouse-face))
+ (setq end (1- pt) beg pt)))
+ (when (and beg end)
+ (setq beg (previous-single-property-change beg 'mouse-face))
+ (setq end (or (next-single-property-change end 'mouse-face) (point-max)))
+ (list (or (get-text-property beg 'completion--string)
+ (buffer-substring beg end))
+ beg end))))
+
+(compat-defun completion-table-with-metadata (table metadata) ;; <compat-tests:completion-table-with-metadata>
+ "Return new completion TABLE with METADATA.
+METADATA should be an alist of completion metadata. See
+`completion-metadata' for a list of supported metadata."
+ (lambda (string pred action)
+ (if (eq action 'metadata)
+ `(metadata . ,metadata)
+ (complete-with-action action table string pred))))
+
+;;;; Defined in subr-x.el
+
+(compat-defun add-remove--display-text-property (start end spec value &optional object remove) ;; <compat-tests:add-display-text-property>
+ "Helper function for `add-display-text-property' and `remove-display-text-property'."
+ (let ((sub-start start)
+ (sub-end 0)
+ (limit (if (stringp object)
+ (min (length object) end)
+ (min end (point-max))))
+ disp)
+ (while (< sub-end end)
+ (setq sub-end (next-single-property-change sub-start 'display object
+ limit))
+ (if (not (setq disp (get-text-property sub-start 'display object)))
+ (unless remove
+ (put-text-property sub-start sub-end 'display (list spec value)
+ object))
+ (let ((changed nil)
+ type)
+ (setq disp
+ (cond
+ ((vectorp disp)
+ (setq type 'vector)
+ (seq-into disp 'list))
+ ((or (not (consp (car-safe disp)))
+ (eq (caar disp) 'margin))
+ (setq type 'scalar)
+ (list disp))
+ (t
+ (setq type 'list)
+ disp)))
+ (when-let* ((old (assoc spec disp)))
+ (setq disp (if (eq type 'list)
+ (remove old disp)
+ (delete old disp))
+ changed t))
+ (unless remove
+ (setq disp (cons (list spec value) disp)
+ changed t))
+ (when changed
+ (if (not disp)
+ (remove-text-properties sub-start sub-end '(display nil) object)
+ (when (eq type 'vector)
+ (setq disp (seq-into disp 'vector)))
+ (put-text-property sub-start sub-end 'display disp object)))))
+ (setq sub-start sub-end))))
+
+(compat-defun remove-display-text-property (start end spec &optional object) ;; <compat-tests:remove-display-text-property>
+ "Remove the display specification SPEC from the text from START to END.
+SPEC is the car of the display specification to remove, e.g. `height'.
+If any text in the region has other display specifications, those specs
+are retained.
+
+OBJECT is either a string or a buffer to remove the specification from.
+If omitted, OBJECT defaults to the current buffer."
+ (add-remove--display-text-property start end spec nil object 'remove))
+
+(compat-defvar work-buffer-limit 10 ;; <compat-tests:with-work-buffer>
+ "Maximum number of reusable work buffers.
+When this limit is exceeded, newly allocated work buffers are
+automatically killed, which means that in a such case
+`with-work-buffer' becomes equivalent to `with-temp-buffer'.")
+
+;; On Emacs 29 and newer `kill-all-local-variables' has a KILL-PERMANENT argument.
+(static-if (< emacs-major-version 29) nil
+ (compat-defvar work-buffer--list nil ;; <compat-tests:with-work-buffer>
+ "List of work buffers.")
+
+ (compat-defun work-buffer--get () ;; <compat-tests:with-work-buffer>
+ "Get a work buffer."
+ (let ((buffer (pop work-buffer--list)))
+ (if (buffer-live-p buffer)
+ buffer
+ (generate-new-buffer " *work*" t))))
+
+ (compat-defun work-buffer--release (buffer) ;; <compat-tests:with-work-buffer>
+ "Release work BUFFER."
+ (if (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (delete-all-overlays))
+ (let (change-major-mode-hook)
+ (setq buffer-read-only nil)
+ (kill-all-local-variables t))
+ (push buffer work-buffer--list)))
+ (when (> (length work-buffer--list) work-buffer-limit)
+ (mapc #'kill-buffer (nthcdr work-buffer-limit work-buffer--list))
+ (setq work-buffer--list (ntake work-buffer-limit work-buffer--list)))))
+
+(compat-defmacro with-work-buffer (&rest body) ;; <compat-tests:with-work-buffer>
+ "Create a work buffer, and evaluate BODY there like `progn'.
+Like `with-temp-buffer', but reuse an already created temporary buffer
+when possible, instead of creating a new one on each call. Avoid
+retaining state referring to a work buffer, and kill any indirect
+buffers you create that use a work buffer as a base."
+ (declare (indent 0) (debug t))
+ (static-if (< emacs-major-version 29)
+ `(with-temp-buffer ,@body)
+ (let ((work-buffer (make-symbol "work-buffer")))
+ `(let ((,work-buffer (work-buffer--get)))
+ (with-current-buffer ,work-buffer
+ (unwind-protect
+ (progn ,@body)
+ (work-buffer--release ,work-buffer)))))))
+
+;;;; Defined in button.el
+
+(compat-defun unbuttonize-region (start end) ;; <compat-tests:buttonize-region>
+ "Remove all the buttons between START and END.
+This removes both text-property and overlay based buttons."
+ (dolist (o (overlays-in start end))
+ (when (overlay-get o 'button)
+ (delete-overlay o)))
+ (with-silent-modifications
+ (remove-text-properties start end (button--properties nil nil nil))
+ (add-face-text-property start end 'button nil)))
+
+(provide 'compat-31)
+;;; compat-31.el ends here