'(ansi-color-names-vector
["black" "red3" "green3" "yellow3" "LightSkyBlue1" "magenta3" "cyan3" "gray90"])
'(aws-snippets-ec2-list-instances-query
- (quote
- ("Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime]" "Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, IamInstanceProfile.Arn]" "Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, KeyName]" "Reservations[].Instances[?IamInstanceProfile.Arn==null].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Type`].Value[] | [0],Tags[?Key==`Project`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, IamInstanceProfile.Arn]")))
- '(aws-snippets-profiles (quote ("test" "prod" "cn")))
- '(aws-snippets-regions
- (quote
- ("us-east-1" "eu-west-1" "ap-southeast-1" "cn-north-1")))
+ '("Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime]" "Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, IamInstanceProfile.Arn]" "Reservations[].Instances[].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Schedule`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, KeyName]" "Reservations[].Instances[?IamInstanceProfile.Arn==null].[Tags[?Key==`Name`].Value[] | [0],Tags[?Key==`Type`].Value[] | [0],Tags[?Key==`Project`].Value[] | [0],InstanceId, State.Name, PublicDnsName, InstanceType,Placement.AvailabilityZone,LaunchTime, IamInstanceProfile.Arn]"))
+ '(aws-snippets-profiles '("test" "prod" "cn"))
+ '(aws-snippets-regions '("us-east-1" "eu-west-1" "ap-southeast-1" "cn-north-1"))
'(blink-cursor-mode t)
'(cfengine-parameters-indent '(promise pname 2))
'(custom-safe-themes
- (quote
- ("8db4b03b9ae654d4a57804286eb3e332725c84d7cdab38463cb6b97d5762ad26" default)))
+ '("8db4b03b9ae654d4a57804286eb3e332725c84d7cdab38463cb6b97d5762ad26" default))
'(gnuserv-program (concat exec-directory "/gnuserv"))
'(grep-template "grep -i <X> <C> -nH -e <R> <F>")
'(helm-comint-mode-list '(comint-mode slime-repl-mode sql-interactive-mode))
'(german-holidays jenkinsfile-mode cider company seq flymake log4e annotate a magit-section bicycle beacon alert graphviz-dot-mode helm-core apache-mode groovy-mode kubel solarized-theme lsp-ui docker-tramp dockerfile-mode yasnippet with-editor treepy transient simple-httpd request markdown-mode magit-popup hydra htmlize hl-todo helm git-commit f elfeed dash-functional dash clomacs clojure-mode async magit org lsp-mode git-link eglot helm-org biblio parsebib parseedn lv yaml-mode xah-lookup x-path-walker visual-regexp visual-ascii-mode vertica-snippets undo-tree twittering-mode tt-mode tldr terraform-mode tabulated-list sx&nbrlcritic flymake-json flycheck fast-scroll expand-region ess epoch-view engine-mode elfeed-org ejc-sql editorconfig edit-indirect dired-narrow dired+ demo-it debian-changelog-mode csv-nav csv-mode command-log-mode color-identifiers-mode centered-cursor-mode bug-hunter aws-snippets avy ascii artbollocks-mode apples-mode ac-helm))
'(package-user-dir "~/.emacs.d/elpa")
'(paradox-github-token t)
- '(safe-local-variable-values (quote ((bba-keep-whitespaces . 1))))
- '(template-default-directories (quote ("~/.emacs.d/templates/")))
+ '(safe-local-variable-values
+ '((user-mail-address . "baronNOSPAMbundesbrandschatzamt.de")
+ (user-mail-address . baron@bundesbrandschatzamt\.de)
+ (bba-keep-whitespaces . 1)))
+ '(template-default-directories '("~/.emacs.d/templates/"))
'(toolbar-visible-p nil)
- '(vc-handled-backends (quote (RCS CVS SVN SCCS Bzr Hg Arch))))
+ '(vc-handled-backends '(RCS CVS SVN SCCS Bzr Hg Arch)))
(defcustom sql-mysql-program "~/bin/mysql"
"*Command to start mysql by mysqlDB."
:type 'file
--- /dev/null
+Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasignNOSPAMelpa.gnu.org> (trust undefined) created at 2022-08-25T23:05:04+0200 using RSA
\ No newline at end of file
--- /dev/null
+;;; Directory Local Variables
+;;; For more information see (info "(emacs) Directory Variables")
+
+((emacs-lisp-mode
+ (byte-compile-docstring-max-column . 100)
+ (show-trailing-whitespace . t)
+ (indent-tabs-mode . nil)))
--- /dev/null
+#+options: toc:nil num:nil
+#+link: compat https://todo.sr.ht/~pkal/compat/
+
+* Release of "Compat" Version 28.1.2.2
+
+This is a minor release that hopes to address [[compat:7]].
+
+(Release <2022-08-25 Thu>)
+
+* Release of "Compat" Version 28.1.2.1
+
+This is a minor release adding the following changes:
+
+- Add =derived-mode-p= defined in Emacs 27
+- Add =provided-mode-derived-p= defined in Emacs 27
+- Add =read-multiple-choice= defined in Emacs 26
+- Add =file-name-absolute-p= defined in Emacs 28
+
+The only other notable change is that the manual has been rewritten to
+include much more documentation that had been the case previously.
+
+(Release <2022-08-24 Wed>)
+
+* Release of "Compat" Version 28.1.2.0
+
+The main change of this release has been the major simplification of
+Compat's initialisation system, improving the situation around issues
+people had been reporting ([[compat:4]], once again) with unconventional
+or unpopular packaging systems.
+
+In addition to this, the following functional changes have been made:
+
+- Fix =format-prompt= of an empty string as "default" argument
+- Add =decoded-time-period= defined in Emacs 28
+- Add =subr-primitive-p= defined in Emacs 28
+
+Minor improvements to manual are also part of this release.
+
+(Release <2022-07-18 Mon>)
+
+* Release of "Compat" Version 28.1.1.3
+
+This release just contains a hot-fix for an issue introduced in the
+last version, where compat.el raises an error during byte compilation.
+See [[compat:4]].
+
+(Release <2022-06-19 Sun>)
+
+* Release of "Compat" Version 28.1.1.2
+
+Two main changes have necessitated a new patch release:
+
+1. Fix issues related to the loading of compat when uncompiled. See
+ [[https://lists.sr.ht/~pkal/compat-devel/%3C20220530191000.2183047-1-jonas%40bernoul.li%3E][this thread]] for more details on the problem.
+2. Fix issues related to the loading of compat on old pre-releases
+ (think of 28.0.50). See [[https://lists.sr.ht/~pkal/compat-devel/%3Cf8635d7d-e233-448f-b325-9e850363241c%40www.fastmail.com%3E][this thread]] for more details on the
+ problem.
+
+(Released <2022-06-22 Wed>)
+
+* Release of "Compat" Version 28.1.1.1
+
+This is a minor release fixing a bug in =json-serialize=, that could
+cause unintended side-effects, not related to packages using Compat
+directly (see [[compat:2]]).
+
+(Released <2022-05-05 Thu>)
+
+* Release of "Compat" Version 28.1.1.0
+
+This release mostly fixes a number of smaller bugs that were not
+identified as of 28.1.0.0. Nevertheless these warrent a version bump,
+as some of these changes a functional. These include:
+
+- The addition of the =file-attribute-*= accessor functions.
+- The addition of =file-attribute-collect=.
+- Improvements to the Texinfo manual (via Jonas Bernoulli's recent
+ work on =ox-texinfo=). For the time being, the Texinfo file is
+ maintained in the repository itself, next to the =MANUAL= file.
+ This might change in the future.
+- Adding a prefix to =string-trim=, =string-trim-left= and
+ =string-trim-right= (i.e. now =compat-string-trim=,
+ =compat-string-trim-left= and =compat-string-trim-right=)
+- Improving the version inference used in the =compat-*= macros.
+ This improves the compile-time optimisation that strips away
+ functions that are known to be defined for a specific version.
+- The addition of generalised variable (=setf=) support for
+ =compat-alist-get=.
+- The addition of =image-property= and generalised variable support
+ for =image-property=.
+- The addition of the function =compat-executable-find=.
+- The addition of the function =compat-dired-get-marked-files=.
+- The addition of the function =exec-path=.
+- The addition of the function =make-lock-file-name=.
+- The addition of the function =null-device=.
+- The addition of the function =time-equal-p=.
+- The addition of the function =date-days-in-month=.
+- Handling out-of-directory byte compilation better.
+- Fixing the usage and edge-cases of =and-let*=.
+
+Furthermore a bug tracker was added: https://todo.sr.ht/~pkal/compat,
+which is the preferred way to report issues or feature requests.
+General problems, questions, etc. are still better discussed on the
+development mailing list: https://lists.sr.ht/~pkal/compat-devel.
+
+(Released <2022-04-22 Fri>)
+
+
--- /dev/null
+;;; compat-24.el --- Compatibility Layer for Emacs 24.4 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; URL: https://git.sr.ht/~pkal/compat/
+;; Keywords: lisp
+
+;; 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:
+
+;; Find here the functionality added in Emacs 24.4, needed by older
+;; versions.
+;;
+;; Only load this library if you need to use one of the following
+;; functions:
+;;
+;; - `compat-='
+;; - `compat-<'
+;; - `compat->'
+;; - `compat-<='
+;; - `compat->='
+;; - `split-string'.
+
+;;; Code:
+
+(require 'compat-macs "compat-macs.el")
+
+(compat-declare-version "24.4")
+
+;;;; Defined in data.c
+
+(compat-defun = (number-or-marker &rest numbers-or-markers)
+ "Handle multiple arguments."
+ :prefix t
+ (catch 'fail
+ (while numbers-or-markers
+ (unless (= number-or-marker (car numbers-or-markers))
+ (throw 'fail nil))
+ (setq number-or-marker (pop numbers-or-markers)))
+ t))
+
+(compat-defun < (number-or-marker &rest numbers-or-markers)
+ "Handle multiple arguments."
+ :prefix t
+ (catch 'fail
+ (while numbers-or-markers
+ (unless (< number-or-marker (car numbers-or-markers))
+ (throw 'fail nil))
+ (setq number-or-marker (pop numbers-or-markers)))
+ t))
+
+(compat-defun > (number-or-marker &rest numbers-or-markers)
+ "Handle multiple arguments."
+ :prefix t
+ (catch 'fail
+ (while numbers-or-markers
+ (unless (> number-or-marker (car numbers-or-markers))
+ (throw 'fail nil))
+ (setq number-or-marker (pop numbers-or-markers)))
+ t))
+
+(compat-defun <= (number-or-marker &rest numbers-or-markers)
+ "Handle multiple arguments."
+ :prefix t
+ (catch 'fail
+ (while numbers-or-markers
+ (unless (<= number-or-marker (car numbers-or-markers))
+ (throw 'fail nil))
+ (setq number-or-marker (pop numbers-or-markers)))
+ t))
+
+(compat-defun >= (number-or-marker &rest numbers-or-markers)
+ "Handle multiple arguments."
+ :prefix t
+ (catch 'fail
+ (while numbers-or-markers
+ (unless (>= number-or-marker (pop numbers-or-markers))
+ (throw 'fail nil)))
+ t))
+
+(compat-defun bool-vector-exclusive-or (a b &optional c)
+ "Return A ^ B, bitwise exclusive or.
+If optional third argument C is given, store result into C.
+A, B, and C must be bool vectors of the same length.
+Return the destination vector if it changed or nil otherwise."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (bool-vector-p b)
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (unless (or (null c) (bool-vector-p c))
+ (signal 'wrong-type-argument (list 'bool-vector-p c)))
+ (when (/= (length a) (length b))
+ (signal 'wrong-length-argument (list (length a) (length b))))
+ (let ((dest (or c (make-bool-vector (length a) nil))) changed)
+ (when (/= (length a) (length dest))
+ (signal 'wrong-length-argument (list (length a) (length dest))))
+ (dotimes (i (length dest))
+ (let ((val (not (eq (aref a i) (aref b i)))))
+ (unless (eq val (aref dest i))
+ (setq changed t))
+ (aset dest i val)))
+ (if c (and changed c) dest)))
+
+(compat-defun bool-vector-union (a b &optional c)
+ "Return A | B, bitwise or.
+If optional third argument C is given, store result into C.
+A, B, and C must be bool vectors of the same length.
+Return the destination vector if it changed or nil otherwise."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (bool-vector-p b)
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (unless (or (null c) (bool-vector-p c))
+ (signal 'wrong-type-argument (list 'bool-vector-p c)))
+ (when (/= (length a) (length b))
+ (signal 'wrong-length-argument (list (length a) (length b))))
+ (let ((dest (or c (make-bool-vector (length a) nil))) changed)
+ (when (/= (length a) (length dest))
+ (signal 'wrong-length-argument (list (length a) (length dest))))
+ (dotimes (i (length dest))
+ (let ((val (or (aref a i) (aref b i))))
+ (unless (eq val (aref dest i))
+ (setq changed t))
+ (aset dest i val)))
+ (if c (and changed c) dest)))
+
+(compat-defun bool-vector-intersection (a b &optional c)
+ "Return A & B, bitwise and.
+If optional third argument C is given, store result into C.
+A, B, and C must be bool vectors of the same length.
+Return the destination vector if it changed or nil otherwise."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (bool-vector-p b)
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (unless (or (null c) (bool-vector-p c))
+ (signal 'wrong-type-argument (list 'bool-vector-p c)))
+ (when (/= (length a) (length b))
+ (signal 'wrong-length-argument (list (length a) (length b))))
+ (let ((dest (or c (make-bool-vector (length a) nil))) changed)
+ (when (/= (length a) (length dest))
+ (signal 'wrong-length-argument (list (length a) (length dest))))
+ (dotimes (i (length dest))
+ (let ((val (and (aref a i) (aref b i))))
+ (unless (eq val (aref dest i))
+ (setq changed t))
+ (aset dest i val)))
+ (if c (and changed c) dest)))
+
+(compat-defun bool-vector-set-difference (a b &optional c)
+ "Return A &~ B, set difference.
+If optional third argument C is given, store result into C.
+A, B, and C must be bool vectors of the same length.
+Return the destination vector if it changed or nil otherwise."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (bool-vector-p b)
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (unless (or (null c) (bool-vector-p c))
+ (signal 'wrong-type-argument (list 'bool-vector-p c)))
+ (when (/= (length a) (length b))
+ (signal 'wrong-length-argument (list (length a) (length b))))
+ (let ((dest (or c (make-bool-vector (length a) nil))) changed)
+ (when (/= (length a) (length dest))
+ (signal 'wrong-length-argument (list (length a) (length dest))))
+ (dotimes (i (length dest))
+ (let ((val (and (aref a i) (not (aref b i)))))
+ (unless (eq val (aref dest i))
+ (setq changed t))
+ (aset dest i val)))
+ (if c (and changed c) dest)))
+
+(compat-defun bool-vector-not (a &optional b)
+ "Compute ~A, set complement.
+If optional second argument B is given, store result into B.
+A and B must be bool vectors of the same length.
+Return the destination vector."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (or (null b) (bool-vector-p b))
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (let ((dest (or b (make-bool-vector (length a) nil))))
+ (when (/= (length a) (length dest))
+ (signal 'wrong-length-argument (list (length a) (length dest))))
+ (dotimes (i (length dest))
+ (aset dest i (not (aref a i))))
+ dest))
+
+(compat-defun bool-vector-subsetp (a b)
+ "Return t if every t value in A is also t in B, nil otherwise.
+A and B must be bool vectors of the same length."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (unless (bool-vector-p b)
+ (signal 'wrong-type-argument (list 'bool-vector-p b)))
+ (when (/= (length a) (length b))
+ (signal 'wrong-length-argument (list (length a) (length b))))
+ (catch 'not-subset
+ (dotimes (i (length a))
+ (when (if (aref a i) (not (aref b i)) nil)
+ (throw 'not-subset nil)))
+ t))
+
+(compat-defun bool-vector-count-consecutive (a b i)
+ "Count how many consecutive elements in A equal B starting at I.
+A is a bool vector, B is t or nil, and I is an index into A."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (setq b (and b t)) ;normalise to nil or t
+ (unless (< i (length a))
+ (signal 'args-out-of-range (list a i)))
+ (let ((len (length a)) (n i))
+ (while (and (< i len) (eq (aref a i) b))
+ (setq i (1+ i)))
+ (- i n)))
+
+(compat-defun bool-vector-count-population (a)
+ "Count how many elements in A are t.
+A is a bool vector. To count A's nil elements, subtract the
+return value from A's length."
+ (unless (bool-vector-p a)
+ (signal 'wrong-type-argument (list 'bool-vector-p a)))
+ (let ((n 0))
+ (dotimes (i (length a))
+ (when (aref a i)
+ (setq n (1+ n))))
+ n))
+
+;;;; Defined in subr.el
+
+;;* UNTESTED
+(compat-defmacro with-eval-after-load (file &rest body)
+ "Execute BODY after FILE is loaded.
+FILE is normally a feature name, but it can also be a file name,
+in case that file does not provide any feature. See `eval-after-load'
+for more details about the different forms of FILE and their semantics."
+ (declare (indent 1) (debug (form def-body)))
+ ;; See https://nullprogram.com/blog/2018/02/22/ on how
+ ;; `eval-after-load' is used to preserve compatibility with 24.3.
+ `(eval-after-load ,file `(funcall ',,`(lambda () ,@body))))
+
+(compat-defun special-form-p (object)
+ "Non-nil if and only if OBJECT is a special form."
+ (if (and (symbolp object) (fboundp object))
+ (setq object (condition-case nil
+ (indirect-function object)
+ (void-function nil))))
+ (and (subrp object) (eq (cdr (subr-arity object)) 'unevalled)))
+
+(compat-defun macrop (object)
+ "Non-nil if and only if OBJECT is a macro."
+ (let ((def (condition-case nil
+ (indirect-function object)
+ (void-function nil))))
+ (when (consp def)
+ (or (eq 'macro (car def))
+ (and (autoloadp def) (memq (nth 4 def) '(macro t)))))))
+
+(compat-defun string-suffix-p (suffix string &optional ignore-case)
+ "Return non-nil if SUFFIX is a suffix of STRING.
+If IGNORE-CASE is non-nil, the comparison is done without paying
+attention to case differences."
+ (let ((start-pos (- (length string) (length suffix))))
+ (and (>= start-pos 0)
+ (eq t (compare-strings suffix nil nil
+ string start-pos nil ignore-case)))))
+
+(compat-defun split-string (string &optional separators omit-nulls trim)
+ "Extend `split-string' by a TRIM argument.
+The remaining arguments STRING, SEPARATORS and OMIT-NULLS are
+handled just as with `split-string'."
+ :prefix t
+ (let* ((token (split-string string separators omit-nulls))
+ (trimmed (if trim
+ (mapcar
+ (lambda (token)
+ (when (string-match (concat "\\`" trim) token)
+ (setq token (substring token (match-end 0))))
+ (when (string-match (concat trim "\\'") token)
+ (setq token (substring token 0 (match-beginning 0))))
+ token)
+ token)
+ token)))
+ (if omit-nulls (delete "" trimmed) trimmed)))
+
+(compat-defun delete-consecutive-dups (list &optional circular)
+ "Destructively remove `equal' consecutive duplicates from LIST.
+First and last elements are considered consecutive if CIRCULAR is
+non-nil."
+ (let ((tail list) last)
+ (while (cdr tail)
+ (if (equal (car tail) (cadr tail))
+ (setcdr tail (cddr tail))
+ (setq last tail
+ tail (cdr tail))))
+ (if (and circular
+ last
+ (equal (car tail) (car list)))
+ (setcdr last nil)))
+ list)
+
+;;* UNTESTED
+(compat-defun define-error (name message &optional parent)
+ "Define NAME as a new error signal.
+MESSAGE is a string that will be output to the echo area if such an error
+is signaled without being caught by a `condition-case'.
+PARENT is either a signal or a list of signals from which it inherits.
+Defaults to `error'."
+ (unless parent (setq parent 'error))
+ (let ((conditions
+ (if (consp parent)
+ (apply #'append
+ (mapcar (lambda (parent)
+ (cons parent
+ (or (get parent 'error-conditions)
+ (error "Unknown signal `%s'" parent))))
+ parent))
+ (cons parent (get parent 'error-conditions)))))
+ (put name 'error-conditions
+ (delete-dups (copy-sequence (cons name conditions))))
+ (when message (put name 'error-message message))))
+
+;;;; Defined in minibuffer.el
+
+;;* UNTESTED
+(compat-defun completion-table-with-cache (fun &optional ignore-case)
+ "Create dynamic completion table from function FUN, with cache.
+This is a wrapper for `completion-table-dynamic' that saves the last
+argument-result pair from FUN, so that several lookups with the
+same argument (or with an argument that starts with the first one)
+only need to call FUN once. This can be useful when FUN performs a
+relatively slow operation, such as calling an external process.
+
+When IGNORE-CASE is non-nil, FUN is expected to be case-insensitive."
+ (let* (last-arg last-result
+ (new-fun
+ (lambda (arg)
+ (if (and last-arg (string-prefix-p last-arg arg ignore-case))
+ last-result
+ (prog1
+ (setq last-result (funcall fun arg))
+ (setq last-arg arg))))))
+ (completion-table-dynamic new-fun)))
+
+;;* UNTESTED
+(compat-defun completion-table-merge (&rest tables)
+ "Create a completion table that collects completions from all TABLES."
+ (lambda (string pred action)
+ (cond
+ ((null action)
+ (let ((retvals (mapcar (lambda (table)
+ (try-completion string table pred))
+ tables)))
+ (if (member string retvals)
+ string
+ (try-completion string
+ (mapcar (lambda (value)
+ (if (eq value t) string value))
+ (delq nil retvals))
+ pred))))
+ ((eq action t)
+ (apply #'append (mapcar (lambda (table)
+ (all-completions string table pred))
+ tables)))
+ (t
+ (completion--some (lambda (table)
+ (complete-with-action action table string pred))
+ tables)))))
+
+;;;; Defined in subr-x.el
+
+;;* UNTESTED
+(compat-advise require (feature &rest args)
+ "Allow for Emacs 24.x to require the inexistent FEATURE subr-x."
+ ;; As the compatibility advise around `require` is more a hack than
+ ;; of of actual value, the highlighting is suppressed.
+ :no-highlight t
+ (if (eq feature 'subr-x)
+ (let ((entry (assq feature after-load-alist)))
+ (let ((load-file-name nil))
+ (dolist (form (cdr entry))
+ (funcall (eval form t)))))
+ (apply oldfun feature args)))
+
+(compat-defun hash-table-keys (hash-table)
+ "Return a list of keys in HASH-TABLE."
+ (let (values)
+ (maphash
+ (lambda (k _v) (push k values))
+ hash-table)
+ values))
+
+(compat-defun 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))
+
+(compat-defun string-empty-p (string)
+ "Check whether STRING is empty."
+ (string= string ""))
+
+(compat-defun string-join (strings &optional separator)
+ "Join all STRINGS using SEPARATOR.
+Optional argument SEPARATOR must be a string, a vector, or a list of
+characters; nil stands for the empty string."
+ (mapconcat #'identity strings separator))
+
+(compat-defun string-blank-p (string)
+ "Check whether STRING is either empty or only whitespace.
+The following characters count as whitespace here: space, tab, newline and
+carriage return."
+ (string-match-p "\\`[ \t\n\r]*\\'" string))
+
+(compat-defun string-remove-prefix (prefix string)
+ "Remove PREFIX from STRING if present."
+ (if (string-prefix-p prefix string)
+ (substring string (length prefix))
+ string))
+
+(compat-defun string-remove-suffix (suffix string)
+ "Remove SUFFIX from STRING if present."
+ (if (string-suffix-p suffix string)
+ (substring string 0 (- (length string) (length suffix)))
+ string))
+
+;;;; Defined in faces.el
+
+;;* UNTESTED
+(compat-defun face-spec-set (face spec &optional spec-type)
+ "Set the FACE's spec SPEC, define FACE, and recalculate its attributes.
+See `defface' for the format of SPEC.
+
+The appearance of each face is controlled by its specs (set via
+this function), and by the internal frame-specific face
+attributes (set via `set-face-attribute').
+
+This function also defines FACE as a valid face name if it is not
+already one, and (re)calculates its attributes on existing
+frames.
+
+The optional argument SPEC-TYPE determines which spec to set:
+ nil, omitted or `face-override-spec' means the override spec,
+ which overrides all the other types of spec mentioned below
+ (this is usually what you want if calling this function
+ outside of Custom code);
+ `customized-face' or `saved-face' means the customized spec or
+ the saved custom spec;
+ `face-defface-spec' means the default spec
+ (usually set only via `defface');
+ `reset' means to ignore SPEC, but clear the `customized-face'
+ and `face-override-spec' specs;
+Any other value means not to set any spec, but to run the
+function for defining FACE and recalculating its attributes."
+ (if (get face 'face-alias)
+ (setq face (get face 'face-alias)))
+ ;; Save SPEC to the relevant symbol property.
+ (unless spec-type
+ (setq spec-type 'face-override-spec))
+ (if (memq spec-type '(face-defface-spec face-override-spec
+ customized-face saved-face))
+ (put face spec-type spec))
+ (if (memq spec-type '(reset saved-face))
+ (put face 'customized-face nil))
+ ;; Setting the face spec via Custom empties out any override spec,
+ ;; similar to how setting a variable via Custom changes its values.
+ (if (memq spec-type '(customized-face saved-face reset))
+ (put face 'face-override-spec nil))
+ ;; If we reset the face based on its custom spec, it is unmodified
+ ;; as far as Custom is concerned.
+ (unless (eq face 'face-override-spec)
+ (put face 'face-modified nil))
+ ;; Initialize the face if it does not exist, then recalculate.
+ (make-empty-face face)
+ (dolist (frame (frame-list))
+ (face-spec-recalc face frame)))
+
+(compat--inhibit-prefixed (provide 'compat-24))
+;;; compat-24.el ends here
--- /dev/null
+;;; compat-25.el --- Compatibility Layer for Emacs 25.1 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; URL: https://git.sr.ht/~pkal/compat/
+;; Keywords: lisp
+
+;; 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:
+
+;; Find here the functionality added in Emacs 25.1, needed by older
+;; versions.
+;;
+;; Only load this library if you need to use one of the following
+;; functions:
+;;
+;; - `compat-sort'
+
+;;; Code:
+
+(require 'compat-macs "compat-macs.el")
+
+(compat-declare-version "25.1")
+
+;;;; Defined in alloc.c
+
+(compat-defun bool-vector (&rest objects)
+ "Return a new bool-vector with specified arguments as elements.
+Allows any number of arguments, including zero.
+usage: (bool-vector &rest OBJECTS)"
+ (let ((vec (make-bool-vector (length objects) nil))
+ (i 0))
+ (while objects
+ (when (car objects)
+ (aset vec i t))
+ (setq objects (cdr objects)
+ i (1+ i)))
+ vec))
+
+;;;; Defined in fns.c
+
+(compat-defun sort (seq predicate)
+ "Extend `sort' to sort SEQ as a vector."
+ :prefix t
+ (cond
+ ((listp seq)
+ (sort seq predicate))
+ ((vectorp seq)
+ (let ((cseq (sort (append seq nil) predicate)))
+ (dotimes (i (length cseq))
+ (setf (aref seq i) (nth i cseq)))
+ (apply #'vector cseq)))
+ ((signal 'wrong-type-argument 'list-or-vector-p))))
+
+;;;; Defined in editfns.c
+
+(compat-defun format-message (string &rest objects)
+ "Format a string out of a format-string and arguments.
+The first argument is a format control string.
+The other arguments are substituted into it to make the result, a string.
+
+This implementation is equivalent to `format'."
+ (apply #'format string objects))
+
+;;;; Defined in minibuf.c
+
+;; TODO advise read-buffer to handle 4th argument
+
+;;;; Defined in fileio.c
+
+(compat-defun directory-name-p (name)
+ "Return non-nil if NAME ends with a directory separator character."
+ :realname compat--directory-name-p
+ (eq (eval-when-compile
+ (if (memq system-type '(cygwin windows-nt ms-dos))
+ ?\\ ?/))
+ (aref name (1- (length name)))))
+
+;;;; Defined in subr.el
+
+(compat-defun string-greaterp (string1 string2)
+ "Return non-nil if STRING1 is greater than STRING2 in lexicographic order.
+Case is significant.
+Symbols are also allowed; their print names are used instead."
+ (string-lessp string2 string1))
+
+;;* UNTESTED
+(compat-defmacro with-file-modes (modes &rest body)
+ "Execute BODY with default file permissions temporarily set to MODES.
+MODES is as for `set-default-file-modes'."
+ (declare (indent 1) (debug t))
+ (let ((umask (make-symbol "umask")))
+ `(let ((,umask (default-file-modes)))
+ (unwind-protect
+ (progn
+ (set-default-file-modes ,modes)
+ ,@body)
+ (set-default-file-modes ,umask)))))
+
+(compat-defun alist-get (key alist &optional default remove testfn)
+ "Find the first element of ALIST whose `car' equals KEY and return its `cdr'.
+If KEY is not found in ALIST, return DEFAULT.
+Equality with KEY is tested by TESTFN, defaulting to `eq'."
+ :realname compat--alist-get-full-elisp
+ (ignore remove)
+ (let (entry)
+ (cond
+ ((or (null testfn) (eq testfn 'eq))
+ (setq entry (assq key alist)))
+ ((eq testfn 'equal)
+ (setq entry (assoc key alist)))
+ ((catch 'found
+ (dolist (ent alist)
+ (when (and (consp ent) (funcall testfn (car ent) key))
+ (throw 'found (setq entry ent))))
+ default)))
+ (if entry (cdr entry) default)))
+
+;;;; Defined in subr-x.el
+
+(compat-defmacro if-let (spec then &rest else)
+ "Bind variables according to SPEC and evaluate THEN or ELSE.
+Evaluate each binding in turn, as in `let*', stopping if a
+binding value is nil. If all are non-nil return the value of
+THEN, otherwise the last form in ELSE.
+
+Each element of SPEC is a list (SYMBOL VALUEFORM) that binds
+SYMBOL to the value of VALUEFORM. An element can additionally be
+of the form (VALUEFORM), which is evaluated and checked for nil;
+i.e. SYMBOL can be omitted if only the test result is of
+interest. It can also be of the form SYMBOL, then the binding of
+SYMBOL is checked for nil.
+
+As a special case, interprets a SPEC of the form \(SYMBOL SOMETHING)
+like \((SYMBOL SOMETHING)). This exists for backward compatibility
+with an old syntax that accepted only one binding."
+ :realname compat--if-let
+ :feature 'subr-x
+ (declare (indent 2)
+ (debug ([&or (symbolp form)
+ (&rest [&or symbolp (symbolp form) (form)])]
+ body)))
+ (when (and (<= (length spec) 2)
+ (not (listp (car spec))))
+ ;; Adjust the single binding case
+ (setq spec (list spec)))
+ `(compat--if-let* ,spec ,then ,(macroexp-progn else)))
+
+(compat-defmacro when-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 are non-nil, return the value of the last form in BODY.
+
+The variable list SPEC is the same as in `if-let'."
+ :feature 'subr-x
+ (declare (indent 1) (debug if-let))
+ `(compat--if-let ,spec ,(macroexp-progn body)))
+
+(compat-defmacro thread-first (&rest forms)
+ "Thread FORMS elements as the first argument of their successor.
+Example:
+ (thread-first
+ 5
+ (+ 20)
+ (/ 25)
+ -
+ (+ 40))
+Is equivalent to:
+ (+ (- (/ (+ 5 20) 25)) 40)
+Note how the single `-' got converted into a list before
+threading."
+ :feature 'subr-x
+ (declare (indent 1)
+ (debug (form &rest [&or symbolp (sexp &rest form)])))
+ (let ((body (car forms)))
+ (dolist (form (cdr forms))
+ (when (symbolp form)
+ (setq form (list form)))
+ (setq body (append (list (car form))
+ (list body)
+ (cdr form))))
+ body))
+
+(compat-defmacro thread-last (&rest forms)
+ "Thread FORMS elements as the last argument of their successor.
+Example:
+ (thread-last
+ 5
+ (+ 20)
+ (/ 25)
+ -
+ (+ 40))
+Is equivalent to:
+ (+ 40 (- (/ 25 (+ 20 5))))
+Note how the single `-' got converted into a list before
+threading."
+ :feature 'subr-x
+ (declare (indent 1) (debug thread-first))
+ (let ((body (car forms)))
+ (dolist (form (cdr forms))
+ (when (symbolp form)
+ (setq form (list form)))
+ (setq body (append form (list body))))
+ body))
+
+;;;; Defined in macroexp.el
+
+(declare-function macrop nil (object))
+(compat-defun macroexpand-1 (form &optional environment)
+ "Perform (at most) one step of macro expansion."
+ :feature 'macroexp
+ (cond
+ ((consp form)
+ (let* ((head (car form))
+ (env-expander (assq head environment)))
+ (if env-expander
+ (if (cdr env-expander)
+ (apply (cdr env-expander) (cdr form))
+ form)
+ (if (not (and (symbolp head) (fboundp head)))
+ form
+ (let ((def (autoload-do-load (symbol-function head) head 'macro)))
+ (cond
+ ;; Follow alias, but only for macros, otherwise we may end up
+ ;; skipping an important compiler-macro (e.g. cl--block-wrapper).
+ ((and (symbolp def) (macrop def)) (cons def (cdr form)))
+ ((not (consp def)) form)
+ (t
+ (if (eq 'macro (car def))
+ (apply (cdr def) (cdr form))
+ form))))))))
+ (t form)))
+
+;;;; Defined in byte-run.el
+
+;;* UNTESTED
+(compat-defun function-put (func prop value)
+ "Set FUNCTION's property PROP to VALUE.
+The namespace for PROP is shared with symbols.
+So far, FUNCTION can only be a symbol, not a lambda expression."
+ :version "24.4"
+ (put func prop value))
+
+;;;; Defined in files.el
+
+;;* UNTESTED
+(compat-defun directory-files-recursively
+ (dir regexp &optional include-directories predicate follow-symlinks)
+ "Return list of all files under directory DIR whose names match REGEXP.
+This function works recursively. Files are returned in \"depth
+first\" order, and files from each directory are sorted in
+alphabetical order. Each file name appears in the returned list
+in its absolute form.
+
+By default, the returned list excludes directories, but if
+optional argument INCLUDE-DIRECTORIES is non-nil, they are
+included.
+
+PREDICATE can be either nil (which means that all subdirectories
+of DIR are descended into), t (which means that subdirectories that
+can't be read are ignored), or a function (which is called with
+the name of each subdirectory, and should return non-nil if the
+subdirectory is to be descended into).
+
+If FOLLOW-SYMLINKS is non-nil, symbolic links that point to
+directories are followed. Note that this can lead to infinite
+recursion."
+ :realname compat--directory-files-recursively
+ (let* ((result nil)
+ (files nil)
+ (dir (directory-file-name dir))
+ ;; When DIR is "/", remote file names like "/method:" could
+ ;; also be offered. We shall suppress them.
+ (tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
+ (dolist (file (sort (file-name-all-completions "" dir)
+ 'string<))
+ (unless (member file '("./" "../"))
+ (if (directory-name-p file)
+ (let* ((leaf (substring file 0 (1- (length file))))
+ (full-file (concat dir "/" leaf)))
+ ;; Don't follow symlinks to other directories.
+ (when (and (or (not (file-symlink-p full-file))
+ (and (file-symlink-p full-file)
+ follow-symlinks))
+ ;; Allow filtering subdirectories.
+ (or (eq predicate nil)
+ (eq predicate t)
+ (funcall predicate full-file)))
+ (let ((sub-files
+ (if (eq predicate t)
+ (condition-case nil
+ (compat--directory-files-recursively
+ full-file regexp include-directories
+ predicate follow-symlinks)
+ (file-error nil))
+ (compat--directory-files-recursively
+ full-file regexp include-directories
+ predicate follow-symlinks))))
+ (setq result (nconc result sub-files))))
+ (when (and include-directories
+ (string-match regexp leaf))
+ (setq result (nconc result (list full-file)))))
+ (when (string-match regexp file)
+ (push (concat dir "/" file) files)))))
+ (nconc result (nreverse files))))
+
+(compat--inhibit-prefixed (provide 'compat-25))
+;;; compat-25.el ends here
--- /dev/null
+;;; compat-26.el --- Compatibility Layer for Emacs 26.1 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; URL: https://git.sr.ht/~pkal/compat/
+;; Keywords: lisp
+
+;; 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:
+
+;; Find here the functionality added in Emacs 26.1, needed by older
+;; versions.
+;;
+;; Only load this library if you need to use one of the following
+;; functions:
+;;
+;; - `compat-sort'
+;; - `line-number-at-pos'
+;; - `compat-alist-get'
+;; - `string-trim-left'
+;; - `string-trim-right'
+;; - `string-trim'
+
+;;; Code:
+
+(require 'compat-macs "compat-macs.el")
+
+(compat-declare-version "26.1")
+
+;;;; Defined in eval.c
+
+(compat-defun func-arity (func)
+ "Return minimum and maximum number of args allowed for FUNC.
+FUNC must be a function of some kind.
+The returned value is a cons cell (MIN . MAX). MIN is the minimum number
+of args. MAX is the maximum number, or the symbol `many', for a
+function with `&rest' args, or `unevalled' for a special form."
+ :realname compat--func-arity
+ (cond
+ ((or (null func) (and (symbolp func) (not (fboundp func))))
+ (signal 'void-function func))
+ ((and (symbolp func) (not (null func)))
+ (compat--func-arity (symbol-function func)))
+ ((eq (car-safe func) 'macro)
+ (compat--func-arity (cdr func)))
+ ((subrp func)
+ (subr-arity func))
+ ((memq (car-safe func) '(closure lambda))
+ ;; See lambda_arity from eval.c
+ (when (eq (car func) 'closure)
+ (setq func (cdr func)))
+ (let ((syms-left (if (consp func)
+ (car func)
+ (signal 'invalid-function func)))
+ (min-args 0) (max-args 0) optional)
+ (catch 'many
+ (dolist (next syms-left)
+ (cond
+ ((not (symbolp next))
+ (signal 'invalid-function func))
+ ((eq next '&rest)
+ (throw 'many (cons min-args 'many)))
+ ((eq next '&optional)
+ (setq optional t))
+ (t (unless optional
+ (setq min-args (1+ min-args)))
+ (setq max-args (1+ max-args)))))
+ (cons min-args max-args))))
+ ((and (byte-code-function-p func) (numberp (aref func 0)))
+ ;; See get_byte_code_arity from bytecode.c
+ (let ((at (aref func 0)))
+ (cons (logand at 127)
+ (if (= (logand at 128) 0)
+ (ash at -8)
+ 'many))))
+ ((and (byte-code-function-p func) (numberp (aref func 0)))
+ ;; See get_byte_code_arity from bytecode.c
+ (let ((at (aref func 0)))
+ (cons (logand at 127)
+ (if (= (logand at 128) 0)
+ (ash at -8)
+ 'many))))
+ ((and (byte-code-function-p func) (listp (aref func 0)))
+ ;; Based on `byte-compile-make-args-desc', this is required for
+ ;; old versions of Emacs that don't use a integer for the argument
+ ;; list description, per e2abe5a13dffb08d6371b6a611bc39c3a9ac2bc6.
+ (let ((arglist (aref func 0)) (mandatory 0) nonrest)
+ (while (and arglist (not (memq (car arglist) '(&optional &rest))))
+ (setq mandatory (1+ mandatory))
+ (setq arglist (cdr arglist)))
+ (setq nonrest mandatory)
+ (when (eq (car arglist) '&optional)
+ (setq arglist (cdr arglist))
+ (while (and arglist (not (eq (car arglist) '&rest)))
+ (setq nonrest (1+ nonrest))
+ (setq arglist (cdr arglist))))
+ (cons mandatory (if arglist 'many nonrest))))
+ ((autoloadp func)
+ (autoload-do-load func)
+ (compat--func-arity func))
+ ((signal 'invalid-function func))))
+
+;;;; Defined in fns.c
+
+(compat-defun assoc (key alist &optional testfn)
+ "Handle the optional argument TESTFN.
+Equality is defined by the function TESTFN, defaulting to
+`equal'. TESTFN is called with 2 arguments: a car of an alist
+element and KEY. With no optional argument, the function behaves
+just like `assoc'."
+ :prefix t
+ (if testfn
+ (catch 'found
+ (dolist (ent alist)
+ (when (funcall testfn (car ent) key)
+ (throw 'found ent))))
+ (assoc key alist)))
+
+(compat-defun mapcan (func sequence)
+ "Apply FUNC to each element of SEQUENCE.
+Concatenate the results by altering them (using `nconc').
+SEQUENCE may be a list, a vector, a boolean vector, or a string."
+ (apply #'nconc (mapcar func sequence)))
+
+;;* UNTESTED
+(compat-defun line-number-at-pos (&optional position absolute)
+ "Handle optional argument ABSOLUTE:
+
+If the buffer is narrowed, the return value by default counts the lines
+from the beginning of the accessible portion of the buffer. But if the
+second optional argument ABSOLUTE is non-nil, the value counts the lines
+from the absolute start of the buffer, disregarding the narrowing."
+ :prefix t
+ (if absolute
+ (save-restriction
+ (widen)
+ (line-number-at-pos position))
+ (line-number-at-pos position)))
+
+;;;; Defined in subr.el
+
+(declare-function compat--alist-get-full-elisp "compat-25"
+ (key alist &optional default remove testfn))
+(compat-defun alist-get (key alist &optional default remove testfn)
+ "Handle TESTFN manually."
+ :realname compat--alist-get-handle-testfn
+ :prefix t
+ (if testfn
+ (compat--alist-get-full-elisp key alist default remove testfn)
+ (alist-get key alist default remove)))
+
+(gv-define-expander compat-alist-get
+ (lambda (do key alist &optional default remove testfn)
+ (macroexp-let2 macroexp-copyable-p k key
+ (gv-letplace (getter setter) alist
+ (macroexp-let2 nil p `(if (and ,testfn (not (eq ,testfn 'eq)))
+ (compat-assoc ,k ,getter ,testfn)
+ (assq ,k ,getter))
+ (funcall do (if (null default) `(cdr ,p)
+ `(if ,p (cdr ,p) ,default))
+ (lambda (v)
+ (macroexp-let2 nil v v
+ (let ((set-exp
+ `(if ,p (setcdr ,p ,v)
+ ,(funcall setter
+ `(cons (setq ,p (cons ,k ,v))
+ ,getter)))))
+ `(progn
+ ,(cond
+ ((null remove) set-exp)
+ ((or (eql v default)
+ (and (eq (car-safe v) 'quote)
+ (eq (car-safe default) 'quote)
+ (eql (cadr v) (cadr default))))
+ `(if ,p ,(funcall setter `(delq ,p ,getter))))
+ (t
+ `(cond
+ ((not (eql ,default ,v)) ,set-exp)
+ (,p ,(funcall setter
+ `(delq ,p ,getter))))))
+ ,v))))))))))
+
+(compat-defun string-trim-left (string &optional regexp)
+ "Trim STRING of leading string matching REGEXP.
+
+REGEXP defaults to \"[ \\t\\n\\r]+\"."
+ :realname compat--string-trim-left
+ :prefix t
+ (if (string-match (concat "\\`\\(?:" (or regexp "[ \t\n\r]+") "\\)") string)
+ (substring string (match-end 0))
+ string))
+
+(compat-defun string-trim-right (string &optional regexp)
+ "Trim STRING of trailing string matching REGEXP.
+
+REGEXP defaults to \"[ \\t\\n\\r]+\"."
+ :realname compat--string-trim-right
+ :prefix t
+ (let ((i (string-match-p
+ (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'")
+ string)))
+ (if i (substring string 0 i) string)))
+
+(compat-defun string-trim (string &optional trim-left trim-right)
+ "Trim STRING of leading with and trailing matching TRIM-LEFT and TRIM-RIGHT.
+
+TRIM-LEFT and TRIM-RIGHT default to \"[ \\t\\n\\r]+\"."
+ :prefix t
+ ;; `string-trim-left' and `string-trim-right' were moved from subr-x
+ ;; to subr in Emacs 27, so to avoid loading subr-x we use the
+ ;; compatibility function here:
+ (compat--string-trim-left
+ (compat--string-trim-right
+ string
+ trim-right)
+ trim-left))
+
+(compat-defun caaar (x)
+ "Return the `car' of the `car' of the `car' of X."
+ (declare (pure t))
+ (car (car (car x))))
+
+(compat-defun caadr (x)
+ "Return the `car' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (car (car (cdr x))))
+
+(compat-defun cadar (x)
+ "Return the `car' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (car (cdr (car x))))
+
+(compat-defun caddr (x)
+ "Return the `car' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (car (cdr (cdr x))))
+
+(compat-defun cdaar (x)
+ "Return the `cdr' of the `car' of the `car' of X."
+ (declare (pure t))
+ (cdr (car (car x))))
+
+(compat-defun cdadr (x)
+ "Return the `cdr' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (car (cdr x))))
+
+(compat-defun cddar (x)
+ "Return the `cdr' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (cdr (cdr (car x))))
+
+(compat-defun cdddr (x)
+ "Return the `cdr' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (cdr (cdr x))))
+
+(compat-defun caaaar (x)
+ "Return the `car' of the `car' of the `car' of the `car' of X."
+ (declare (pure t))
+ (car (car (car (car x)))))
+
+(compat-defun caaadr (x)
+ "Return the `car' of the `car' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (car (car (car (cdr x)))))
+
+(compat-defun caadar (x)
+ "Return the `car' of the `car' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (car (car (cdr (car x)))))
+
+(compat-defun caaddr (x)
+ "Return the `car' of the `car' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (car (car (cdr (cdr x)))))
+
+(compat-defun cadaar (x)
+ "Return the `car' of the `cdr' of the `car' of the `car' of X."
+ (declare (pure t))
+ (car (cdr (car (car x)))))
+
+(compat-defun cadadr (x)
+ "Return the `car' of the `cdr' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (car (cdr (car (cdr x)))))
+
+(compat-defun caddar (x)
+ "Return the `car' of the `cdr' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (car (cdr (cdr (car x)))))
+
+(compat-defun cadddr (x)
+ "Return the `car' of the `cdr' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (car (cdr (cdr (cdr x)))))
+
+(compat-defun cdaaar (x)
+ "Return the `cdr' of the `car' of the `car' of the `car' of X."
+ (declare (pure t))
+ (cdr (car (car (car x)))))
+
+(compat-defun cdaadr (x)
+ "Return the `cdr' of the `car' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (car (car (cdr x)))))
+
+(compat-defun cdadar (x)
+ "Return the `cdr' of the `car' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (cdr (car (cdr (car x)))))
+
+(compat-defun cdaddr (x)
+ "Return the `cdr' of the `car' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (car (cdr (cdr x)))))
+
+(compat-defun cddaar (x)
+ "Return the `cdr' of the `cdr' of the `car' of the `car' of X."
+ (declare (pure t))
+ (cdr (cdr (car (car x)))))
+
+(compat-defun cddadr (x)
+ "Return the `cdr' of the `cdr' of the `car' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (cdr (car (cdr x)))))
+
+(compat-defun cdddar (x)
+ "Return the `cdr' of the `cdr' of the `cdr' of the `car' of X."
+ (declare (pure t))
+ (cdr (cdr (cdr (car x)))))
+
+(compat-defun cddddr (x)
+ "Return the `cdr' of the `cdr' of the `cdr' of the `cdr' of X."
+ (declare (pure t))
+ (cdr (cdr (cdr (cdr x)))))
+
+(compat-defvar gensym-counter 0
+ "Number used to construct the name of the next symbol created by `gensym'.")
+
+(compat-defun gensym (&optional prefix)
+ "Return a new uninterned symbol.
+The name is made by appending `gensym-counter' to PREFIX.
+PREFIX is a string, and defaults to \"g\"."
+ (let ((num (prog1 gensym-counter
+ (setq gensym-counter
+ (1+ gensym-counter)))))
+ (make-symbol (format "%s%d" (or prefix "g") num))))
+
+;;;; Defined in files.el
+
+(declare-function temporary-file-directory nil)
+
+;;* UNTESTED
+(compat-defun make-nearby-temp-file (prefix &optional dir-flag suffix)
+ "Create a temporary file as close as possible to `default-directory'.
+If PREFIX is a relative file name, and `default-directory' is a
+remote file name or located on a mounted file systems, the
+temporary file is created in the directory returned by the
+function `temporary-file-directory'. Otherwise, the function
+`make-temp-file' is used. PREFIX, DIR-FLAG and SUFFIX have the
+same meaning as in `make-temp-file'."
+ (let ((handler (find-file-name-handler
+ default-directory 'make-nearby-temp-file)))
+ (if (and handler (not (file-name-absolute-p default-directory)))
+ (funcall handler 'make-nearby-temp-file prefix dir-flag suffix)
+ (let ((temporary-file-directory (temporary-file-directory)))
+ (make-temp-file prefix dir-flag suffix)))))
+
+(compat-defvar mounted-file-systems
+ (eval-when-compile
+ (if (memq system-type '(windows-nt cygwin))
+ "^//[^/]+/"
+ (concat
+ "^" (regexp-opt '("/afs/" "/media/" "/mnt" "/net/" "/tmp_mnt/")))))
+ "File systems that ought to be mounted.")
+
+(compat-defun file-local-name (file)
+ "Return the local name component of FILE.
+This function removes from FILE the specification of the remote host
+and the method of accessing the host, leaving only the part that
+identifies FILE locally on the remote system.
+The returned file name can be used directly as argument of
+`process-file', `start-file-process', or `shell-command'."
+ :realname compat--file-local-name
+ (or (file-remote-p file 'localname) file))
+
+(compat-defun file-name-quoted-p (name &optional top)
+ "Whether NAME is quoted with prefix \"/:\".
+If NAME is a remote file name and TOP is nil, check the local part of NAME."
+ :realname compat--file-name-quoted-p
+ (let ((file-name-handler-alist (unless top file-name-handler-alist)))
+ (string-prefix-p "/:" (compat--file-local-name name))))
+
+(compat-defun file-name-quote (name &optional top)
+ "Add the quotation prefix \"/:\" to file NAME.
+If NAME is a remote file name and TOP is nil, the local part of
+NAME is quoted. If NAME is already a quoted file name, NAME is
+returned unchanged."
+ (let ((file-name-handler-alist (unless top file-name-handler-alist)))
+ (if (compat--file-name-quoted-p name top)
+ name
+ (concat (file-remote-p name) "/:" (compat--file-local-name name)))))
+
+;;* UNTESTED
+(compat-defun temporary-file-directory ()
+ "The directory for writing temporary files.
+In case of a remote `default-directory', this is a directory for
+temporary files on that remote host. If such a directory does
+not exist, or `default-directory' ought to be located on a
+mounted file system (see `mounted-file-systems'), the function
+returns `default-directory'.
+For a non-remote and non-mounted `default-directory', the value of
+the variable `temporary-file-directory' is returned."
+ (let ((handler (find-file-name-handler
+ default-directory 'temporary-file-directory)))
+ (if handler
+ (funcall handler 'temporary-file-directory)
+ (if (string-match mounted-file-systems default-directory)
+ default-directory
+ temporary-file-directory))))
+
+;;* UNTESTED
+(compat-defun file-attribute-type (attributes)
+ "The type field in ATTRIBUTES returned by `file-attributes'.
+The value is either t for directory, string (name linked to) for
+symbolic link, or nil."
+ (nth 0 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-link-number (attributes)
+ "Return the number of links in ATTRIBUTES returned by `file-attributes'."
+ (nth 1 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-user-id (attributes)
+ "The UID field in ATTRIBUTES returned by `file-attributes'.
+This is either a string or a number. If a string value cannot be
+looked up, a numeric value, either an integer or a float, is
+returned."
+ (nth 2 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-group-id (attributes)
+ "The GID field in ATTRIBUTES returned by `file-attributes'.
+This is either a string or a number. If a string value cannot be
+looked up, a numeric value, either an integer or a float, is
+returned."
+ (nth 3 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-access-time (attributes)
+ "The last access time in ATTRIBUTES returned by `file-attributes'.
+This a Lisp timestamp in the style of `current-time'."
+ (nth 4 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-modification-time (attributes)
+ "The modification time in ATTRIBUTES returned by `file-attributes'.
+This is the time of the last change to the file's contents, and
+is a Lisp timestamp in the style of `current-time'."
+ (nth 5 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-status-change-time (attributes)
+ "The status modification time in ATTRIBUTES returned by `file-attributes'.
+This is the time of last change to the file's attributes: owner
+and group, access mode bits, etc., and is a Lisp timestamp in the
+style of `current-time'."
+ (nth 6 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-size (attributes)
+ "The integer size (in bytes) in ATTRIBUTES returned by `file-attributes'."
+ (nth 7 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-modes (attributes)
+ "The file modes in ATTRIBUTES returned by `file-attributes'.
+This is a string of ten letters or dashes as in ls -l."
+ (nth 8 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-inode-number (attributes)
+ "The inode number in ATTRIBUTES returned by `file-attributes'.
+It is a nonnegative integer."
+ (nth 10 attributes))
+
+;;* UNTESTED
+(compat-defun file-attribute-device-number (attributes)
+ "The file system device number in ATTRIBUTES returned by `file-attributes'.
+It is an integer."
+ (nth 11 attributes))
+
+(compat-defun file-attribute-collect (attributes &rest attr-names)
+ "Return a sublist of ATTRIBUTES returned by `file-attributes'.
+ATTR-NAMES are symbols with the selected attribute names.
+
+Valid attribute names are: type, link-number, user-id, group-id,
+access-time, modification-time, status-change-time, size, modes,
+inode-number and device-number."
+ (let ((idx '((type . 0)
+ (link-number . 1)
+ (user-id . 2)
+ (group-id . 3)
+ (access-time . 4)
+ (modification-time . 5)
+ (status-change-time . 6)
+ (size . 7)
+ (modes . 8)
+ (inode-number . 10)
+ (device-number . 11)))
+ result)
+ (while attr-names
+ (let ((attr (pop attr-names)))
+ (if (assq attr idx)
+ (push (nth (cdr (assq attr idx))
+ attributes)
+ result)
+ (error "Wrong attribute name '%S'" attr))))
+ (nreverse result)))
+
+;;;; Defined in subr-x.el
+
+(compat-defmacro if-let* (varlist then &rest else)
+ "Bind variables according to VARLIST and evaluate THEN or ELSE.
+This is like `if-let' but doesn't handle a VARLIST of the form
+\(SYMBOL SOMETHING) specially."
+ :realname compat--if-let*
+ :feature 'subr-x
+ (declare (indent 2)
+ (debug ((&rest [&or symbolp (symbolp form) (form)])
+ body)))
+ (let ((empty (make-symbol "s"))
+ (last t) list)
+ (dolist (var varlist)
+ (push `(,(if (cdr var) (car var) empty)
+ (and ,last ,(or (cadr var) (car var))))
+ list)
+ (when (or (cdr var) (consp (car var)))
+ (setq last (caar list))))
+ `(let* ,(nreverse list)
+ (if ,(caar list) ,then ,@else))))
+
+(compat-defmacro when-let* (varlist &rest body)
+ "Bind variables according to VARLIST and conditionally evaluate BODY.
+This is like `when-let' but doesn't handle a VARLIST of the form
+\(SYMBOL SOMETHING) specially."
+ ;; :feature 'subr-x
+ (declare (indent 1) (debug if-let*))
+ (let ((empty (make-symbol "s"))
+ (last t) list)
+ (dolist (var varlist)
+ (push `(,(if (cdr var) (car var) empty)
+ (and ,last ,(or (cadr var) (car var))))
+ list)
+ (when (or (cdr var) (consp (car var)))
+ (setq last (caar list))))
+ `(let* ,(nreverse list)
+ (when ,(caar list) ,@body))))
+
+(compat-defmacro and-let* (varlist &rest body)
+ "Bind variables according to VARLIST and conditionally evaluate BODY.
+Like `when-let*', except if BODY is empty and all the bindings
+are non-nil, then the result is non-nil."
+ :feature 'subr-x
+ (declare (indent 1) (debug if-let*))
+ (let ((empty (make-symbol "s"))
+ (last t) list)
+ (dolist (var varlist)
+ (push `(,(if (cdr var) (car var) empty)
+ (and ,last ,(or (cadr var) (car var))))
+ list)
+ (when (or (cdr var) (consp (car var)))
+ (setq last (caar list))))
+ `(let* ,(nreverse list)
+ (if ,(caar list) ,(macroexp-progn (or body '(t)))))))
+
+;;;; Defined in image.el
+
+;;* UNTESTED
+(compat-defun image-property (image property)
+ "Return the value of PROPERTY in IMAGE.
+Properties can be set with
+
+ (setf (image-property IMAGE PROPERTY) VALUE)
+
+If VALUE is nil, PROPERTY is removed from IMAGE."
+ (plist-get (cdr image) property))
+
+;;* UNTESTED
+(unless (get 'image-property 'gv-expander)
+ (gv-define-setter image-property (image property value)
+ (let ((image* (make-symbol "image"))
+ (property* (make-symbol "property"))
+ (value* (make-symbol "value")))
+ `(let ((,image* ,image)
+ (,property* ,property)
+ (,value* ,value))
+ (if
+ (null ,value*)
+ (while
+ (cdr ,image*)
+ (if
+ (eq
+ (cadr ,image*)
+ ,property*)
+ (setcdr ,image*
+ (cdddr ,image*))
+ (setq ,image*
+ (cddr ,image*))))
+ (setcdr ,image*
+ (plist-put
+ (cdr ,image*)
+ ,property* ,value*)))))))
+
+;;;; Defined in rmc.el
+
+;;*UNTESTED
+(compat-defun read-multiple-choice
+ (prompt choices &optional _help-string _show-help long-form)
+ "Ask user to select an entry from CHOICES, promting with PROMPT.
+This function allows to ask the user a multiple-choice question.
+
+CHOICES should be a list of the form (KEY NAME [DESCRIPTION]).
+KEY is a character the user should type to select the entry.
+NAME is a short name for the entry to be displayed while prompting
+\(if there's no room, it might be shortened).
+
+If LONG-FORM, do a `completing-read' over the NAME elements in
+CHOICES instead."
+ :note "This is a partial implementation of `read-multiple-choice', that
+among other things doesn't offer any help and ignores the
+optional DESCRIPTION field."
+ (if long-form
+ (let ((options (mapconcat #'cadr choices "/"))
+ choice)
+ (setq prompt (concat prompt " (" options "): "))
+ (setq choice (completing-read prompt (mapcar #'cadr choices) nil t))
+ (catch 'found
+ (dolist (option choices)
+ (when (string= choice (cadr option))
+ (throw 'found option)))
+ (error "Invalid choice")))
+ (let ((options
+ (mapconcat
+ (lambda (opt)
+ (format
+ "[%s] %s"
+ (key-description (string (car opt)))
+ (cadr opt)))
+ choices " "))
+ choice)
+ (setq prompt (concat prompt " (" options "): "))
+ (while (not (setq choice (assq (read-char prompt) choices)))
+ (message "Invalid choice")
+ (sit-for 1))
+ choice)))
+
+(compat--inhibit-prefixed (provide 'compat-26))
+;;; compat-26.el ends here
--- /dev/null
+;;; compat-27.el --- Compatibility Layer for Emacs 27.1 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; URL: https://git.sr.ht/~pkal/compat/
+;; Keywords: lisp
+
+;; 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:
+
+;; Find here the functionality added in Emacs 27.1, needed by older
+;; versions.
+;;
+;; Only load this library if you need to use one of the following
+;; functions or macros:
+;;
+;; - `compat-recenter'
+;; - `compat-lookup-key'
+;; - `compat-setq-local'
+;; - `compat-assoc-delete-all'
+;; - `compat-file-size-human-readable'
+;; - `compat-executable-find'
+;; - `compat-regexp-opt'
+;; - `compat-dired-get-marked-files'
+
+;;; Code:
+
+(require 'compat-macs "compat-macs.el")
+
+(compat-declare-version "27.1")
+
+;;;; Defined in fns.c
+
+(compat-defun proper-list-p (object)
+ "Return OBJECT's length if it is a proper list, nil otherwise.
+A proper list is neither circular nor dotted (i.e., its last cdr
+is nil)."
+ :min-version "26.1"
+ :max-version "26.3"
+ :realname compat--proper-list-p-length-signal
+ (condition-case nil
+ (and (listp object) (length object))
+ (wrong-type-argument nil)
+ (circular-list nil)))
+
+(compat-defun proper-list-p (object)
+ "Return OBJECT's length if it is a proper list, nil otherwise.
+A proper list is neither circular nor dotted (i.e., its last cdr
+is nil)."
+ :max-version "25.3"
+ :realname compat--proper-list-p-tortoise-hare
+ (when (listp object)
+ (catch 'cycle
+ (let ((hare object) (tortoise object)
+ (max 2) (q 2))
+ (while (consp hare)
+ (setq hare (cdr hare))
+ (when (and (or (/= 0 (setq q (1- q)))
+ (ignore
+ (setq max (ash max 1)
+ q max
+ tortoise hare)))
+ (eq hare tortoise))
+ (throw 'cycle nil)))
+ (and (null hare) (length object))))))
+
+(compat-defun string-distance (string1 string2 &optional bytecompare)
+ "Return Levenshtein distance between STRING1 and STRING2.
+The distance is the number of deletions, insertions, and substitutions
+required to transform STRING1 into STRING2.
+If BYTECOMPARE is nil or omitted, compute distance in terms of characters.
+If BYTECOMPARE is non-nil, compute distance in terms of bytes.
+Letter-case is significant, but text properties are ignored."
+ ;; https://en.wikipedia.org/wiki/Levenshtein_distance
+ (let ((s1 (if bytecompare
+ (encode-coding-string string1 'raw-text)
+ (concat string1 "")))
+ (s2 (if bytecompare
+ (encode-coding-string string2 'raw-text)
+ string2)))
+ (let* ((len1 (length s1))
+ (len2 (length s2))
+ (column (make-vector (1+ len1) 0)))
+ (dotimes (y len1)
+ (setf (aref column (1+ y)) y))
+ (dotimes (x len2)
+ (setf (aref column 0) (1+ x))
+ (let ((lastdiag x) olddiag)
+ (dotimes (y len1)
+ (setf olddiag (aref column (1+ y))
+ (aref column (1+ y))
+ (min (+ (if (= (aref s1 y) (aref s2 x)) 0 1)
+ lastdiag)
+ (1+ (aref column (1+ y)))
+ (1+ (aref column y)))
+ lastdiag olddiag))))
+ (aref column len1))))
+
+;;;; Defined in window.c
+
+(compat-defun recenter (&optional arg redisplay)
+ "Handle optional argument REDISPLAY."
+ :prefix t
+ (recenter arg)
+ (when (and redisplay recenter-redisplay)
+ (redisplay)))
+
+;;;; Defined in keymap.c
+
+(compat-defun lookup-key (keymap key &optional accept-default)
+ "Allow for KEYMAP to be a list of keymaps."
+ :prefix t
+ (cond
+ ((keymapp keymap)
+ (lookup-key keymap key accept-default))
+ ((listp keymap)
+ (catch 'found
+ (dolist (map keymap)
+ (let ((fn (lookup-key map key accept-default)))
+ (when fn (throw 'found fn))))))
+ ((signal 'wrong-type-argument (list 'keymapp keymap)))))
+
+;;;; Defined in json.c
+
+(declare-function json-parse-string nil (string &rest args))
+(declare-function json-encode "json" (object))
+(declare-function json-read-from-string "json" (string))
+(declare-function json-read "json" ())
+(defvar json-encoding-pretty-print)
+(defvar json-object-type)
+(defvar json-array-type)
+(defvar json-false)
+(defvar json-null)
+
+;; The function is declared to satisfy the byte compiler while testing
+;; if native JSON parsing is available.;
+(declare-function json-serialize nil (object &rest args))
+(compat-defun json-serialize (object &rest args)
+ "Return the JSON representation of OBJECT as a string.
+
+OBJECT must be t, a number, string, vector, hashtable, alist, plist,
+or the Lisp equivalents to the JSON null and false values, and its
+elements must recursively consist of the same kinds of values. t will
+be converted to the JSON true value. Vectors will be converted to
+JSON arrays, whereas hashtables, alists and plists are converted to
+JSON objects. Hashtable keys must be strings without embedded null
+characters and must be unique within each object. Alist and plist
+keys must be symbols; if a key is duplicate, the first instance is
+used.
+
+The Lisp equivalents to the JSON null and false values are
+configurable in the arguments ARGS, a list of keyword/argument pairs:
+
+The keyword argument `:null-object' specifies which object to use
+to represent a JSON null value. It defaults to `:null'.
+
+The keyword argument `:false-object' specifies which object to use to
+represent a JSON false value. It defaults to `:false'.
+
+In you specify the same value for `:null-object' and `:false-object',
+a potentially ambiguous situation, the JSON output will not contain
+any JSON false values."
+ :cond (not (condition-case nil
+ (equal (json-serialize '()) "{}")
+ (:success t)
+ (void-function nil)
+ (json-unavailable nil)))
+ :realname compat--json-serialize
+ (require 'json)
+ (letrec ((fix (lambda (obj)
+ (cond
+ ((hash-table-p obj)
+ (let ((ht (copy-hash-table obj)))
+ (maphash
+ (lambda (key val)
+ (unless (stringp key)
+ (signal
+ 'wrong-type-argument
+ (list 'stringp key)))
+ (puthash key (funcall fix val) ht))
+ obj)
+ ht))
+ ((and (listp obj) (consp (car obj))) ;alist
+ (mapcar
+ (lambda (ent)
+ (cons (symbol-name (car ent))
+ (funcall fix (cdr ent))))
+ obj))
+ ((listp obj) ;plist
+ (let (alist)
+ (while obj
+ (push (cons (cond
+ ((keywordp (car obj))
+ (substring
+ (symbol-name (car obj))
+ 1))
+ ((symbolp (car obj))
+ (symbol-name (car obj)))
+ ((signal
+ 'wrong-type-argument
+ (list 'symbolp (car obj)))))
+ (funcall fix (cadr obj)))
+ alist)
+ (unless (consp (cdr obj))
+ (signal 'wrong-type-argument '(consp nil)))
+ (setq obj (cddr obj)))
+ (nreverse alist)))
+ ((vectorp obj)
+ (let ((vec (make-vector (length obj) nil)))
+ (dotimes (i (length obj))
+ (aset vec i (funcall fix (aref obj i))))
+ vec))
+ (obj))))
+ (json-encoding-pretty-print nil)
+ (json-false (or (plist-get args :false-object) :false))
+ (json-null (or (plist-get args :null-object) :null)))
+ (json-encode (funcall fix object))))
+
+(compat-defun json-insert (object &rest args)
+ "Insert the JSON representation of OBJECT before point.
+This is the same as (insert (json-serialize OBJECT)), but potentially
+faster. See the function `json-serialize' for allowed values of
+OBJECT."
+ :cond (not (condition-case nil
+ (equal (json-serialize '()) "{}")
+ (:success t)
+ (void-function nil)
+ (json-unavailable nil)))
+ (insert (apply #'compat--json-serialize object args)))
+
+(compat-defun json-parse-string (string &rest args)
+ "Parse the JSON STRING into a Lisp object.
+This is essentially the reverse operation of `json-serialize', which
+see. The returned object will be the JSON null value, the JSON false
+value, t, a number, a string, a vector, a list, a hashtable, an alist,
+or a plist. Its elements will be further objects of these types. If
+there are duplicate keys in an object, all but the last one are
+ignored. If STRING doesn't contain a valid JSON object, this function
+signals an error of type `json-parse-error'.
+
+The arguments ARGS are a list of keyword/argument pairs:
+
+The keyword argument `:object-type' specifies which Lisp type is used
+to represent objects; it can be `hash-table', `alist' or `plist'. It
+defaults to `hash-table'.
+
+The keyword argument `:array-type' specifies which Lisp type is used
+to represent arrays; it can be `array' (the default) or `list'.
+
+The keyword argument `:null-object' specifies which object to use
+to represent a JSON null value. It defaults to `:null'.
+
+The keyword argument `:false-object' specifies which object to use to
+represent a JSON false value. It defaults to `:false'."
+ :cond (not (condition-case nil
+ (equal (json-serialize '()) "{}")
+ (:success t)
+ (void-function nil)
+ (json-unavailable nil)))
+ (require 'json)
+ (condition-case err
+ (let ((json-object-type (or (plist-get args :object-type) 'hash-table))
+ (json-array-type (or (plist-get args :array-type) 'vector))
+ (json-false (or (plist-get args :false-object) :false))
+ (json-null (or (plist-get args :null-object) :null)))
+ (when (eq json-array-type 'array)
+ (setq json-array-type 'vector))
+ (json-read-from-string string))
+ (json-error (signal 'json-parse-error err))))
+
+(compat-defun json-parse-buffer (&rest args)
+ "Read JSON object from current buffer starting at point.
+Move point after the end of the object if parsing was successful.
+On error, don't move point.
+
+The returned object will be a vector, list, hashtable, alist, or
+plist. Its elements will be the JSON null value, the JSON false
+value, t, numbers, strings, or further vectors, lists, hashtables,
+alists, or plists. If there are duplicate keys in an object, all
+but the last one are ignored.
+
+If the current buffer doesn't contain a valid JSON object, the
+function signals an error of type `json-parse-error'.
+
+The arguments ARGS are a list of keyword/argument pairs:
+
+The keyword argument `:object-type' specifies which Lisp type is used
+to represent objects; it can be `hash-table', `alist' or `plist'. It
+defaults to `hash-table'.
+
+The keyword argument `:array-type' specifies which Lisp type is used
+to represent arrays; it can be `array' (the default) or `list'.
+
+The keyword argument `:null-object' specifies which object to use
+to represent a JSON null value. It defaults to `:null'.
+
+The keyword argument `:false-object' specifies which object to use to
+represent a JSON false value. It defaults to `:false'."
+ :cond (not (condition-case nil
+ (equal (json-serialize '()) "{}")
+ (:success t)
+ (void-function nil)
+ (json-unavailable nil)))
+ (require 'json)
+ (condition-case err
+ (let ((json-object-type (or (plist-get args :object-type) 'hash-table))
+ (json-array-type (or (plist-get args :array-type) 'vector))
+ (json-false (or (plist-get args :false-object) :false))
+ (json-null (or (plist-get args :null-object) :null)))
+ (when (eq json-array-type 'array)
+ (setq json-array-type 'vector))
+ (json-read))
+ (json-error (signal 'json-parse-buffer err))))
+
+;;;; Defined in timefns.c
+
+(compat-defun time-equal-p (t1 t2)
+ "Return non-nil if time value T1 is equal to time value T2.
+A nil value for either argument stands for the current time."
+ :note "This function is not as accurate as the actual `time-equal-p'."
+ (cond
+ ((eq t1 t2))
+ ((and (consp t1) (consp t2))
+ (equal t1 t2))
+ ((let ((now (current-time)))
+ ;; Due to inaccuracies and the relatively slow evaluating of
+ ;; Emacs Lisp compared to C, we allow for slight inaccuracies
+ ;; (less than a millisecond) when comparing time values.
+ (< (abs (- (float-time (or t1 now))
+ (float-time (or t2 now))))
+ 1e-5)))))
+
+;;;; Defined in fileio.c
+
+(compat-defun file-name-absolute-p (filename)
+ "Return t if FILENAME is an absolute file name.
+On Unix, absolute file names start with `/'. In Emacs, an absolute
+file name can also start with an initial `~' or `~USER' component,
+where USER is a valid login name."
+ ;; See definitions in filename.h
+ (let ((seperator
+ (eval-when-compile
+ (if (memq system-type '(cygwin windows-nt ms-dos))
+ "[\\/]" "/")))
+ (drive
+ (eval-when-compile
+ (cond
+ ((memq system-type '(windows-nt ms-dos))
+ "\\`[A-Za-z]:[\\/]")
+ ((eq system-type 'cygwin)
+ "\\`\\([\\/]\\|[A-Za-z]:\\)")
+ ("\\`/"))))
+ (home
+ (eval-when-compile
+ (if (memq system-type '(cygwin windows-nt ms-dos))
+ "\\`~[\\/]" "\\`~/")))
+ (user-home
+ (eval-when-compile
+ (format "\\`\\(~.*?\\)\\(%s.*\\)?$"
+ (if (memq system-type '(cygwin windows-nt ms-dos))
+ "[\\/]" "/")))))
+ (or (and (string-match-p drive filename) t)
+ (and (string-match-p home filename) t)
+ (save-excursion
+ (when (string-match user-home filename)
+ (let ((init (match-string 1 filename)))
+ (not (string=
+ (file-name-base (expand-file-name init))
+ init))))))))
+
+;;;; Defined in subr.el
+
+(compat-defmacro setq-local (&rest pairs)
+ "Handle multiple assignments."
+ :prefix t
+ (unless (zerop (mod (length pairs) 2))
+ (error "PAIRS must have an even number of variable/value members"))
+ (let (body)
+ (while pairs
+ (let* ((sym (pop pairs))
+ (val (pop pairs)))
+ (unless (symbolp sym)
+ (error "Attempting to set a non-symbol: %s" (car pairs)))
+ (push `(set (make-local-variable ,sym) ,val)
+ body)))
+ (cons 'progn (nreverse body))))
+
+(compat-defun provided-mode-derived-p (mode &rest modes)
+ "Non-nil if MODE is derived from one of MODES.
+Uses the `derived-mode-parent' property of the symbol to trace backwards.
+If you just want to check `major-mode', use `derived-mode-p'."
+ :realname compat--provided-mode-derived-p
+ ;; If MODE is an alias, then look up the real mode function first.
+ (let ((alias (symbol-function mode)))
+ (when (and alias (symbolp alias))
+ (setq mode alias)))
+ (while
+ (and
+ (not (memq mode modes))
+ (let* ((parent (get mode 'derived-mode-parent))
+ (parentfn (symbol-function parent)))
+ (setq mode (if (and parentfn (symbolp parentfn)) parentfn parent)))))
+ mode)
+
+;;* UNTESTED
+(defun derived-mode-p (&rest modes)
+ "Non-nil if the current major mode is derived from one of MODES.
+Uses the `derived-mode-parent' property of the symbol to trace backwards."
+ (apply #'compat--provided-mode-derived-p major-mode modes))
+
+;;* UNTESTED
+(compat-defmacro ignore-error (condition &rest body)
+ "Execute BODY; if the error CONDITION occurs, return nil.
+Otherwise, return result of last form in BODY.
+
+CONDITION can also be a list of error conditions."
+ (declare (debug t) (indent 1))
+ `(condition-case nil (progn ,@body) (,condition nil)))
+
+;;* UNTESTED
+(compat-defmacro dolist-with-progress-reporter (spec reporter-or-message &rest body)
+ "Loop over a list and report progress in the echo area.
+Evaluate BODY with VAR bound to each car from LIST, in turn.
+Then evaluate RESULT to get return value, default nil.
+
+REPORTER-OR-MESSAGE is a progress reporter object or a string. In the latter
+case, use this string to create a progress reporter.
+
+At each iteration, print the reporter message followed by progress
+percentage in the echo area. After the loop is finished,
+print the reporter message followed by the word \"done\".
+
+\(fn (VAR LIST [RESULT]) REPORTER-OR-MESSAGE BODY...)"
+ (declare (indent 2) (debug ((symbolp form &optional form) form body)))
+ (let ((prep (make-symbol "--dolist-progress-reporter--"))
+ (count (make-symbol "--dolist-count--"))
+ (list (make-symbol "--dolist-list--")))
+ `(let ((,prep ,reporter-or-message)
+ (,count 0)
+ (,list ,(cadr spec)))
+ (when (stringp ,prep)
+ (setq ,prep (make-progress-reporter ,prep 0 (1- (length ,list)))))
+ (dolist (,(car spec) ,list)
+ ,@body
+ (progress-reporter-update ,prep (setq ,count (1+ ,count))))
+ (progress-reporter-done ,prep)
+ (or ,@(cdr (cdr spec)) nil))))
+
+(compat-defun flatten-tree (tree)
+ "Return a \"flattened\" copy of TREE.
+In other words, return a list of the non-nil terminal nodes, or
+leaves, of the tree of cons cells rooted at TREE. Leaves in the
+returned list are in the same order as in TREE.
+
+\(flatten-tree \\='(1 (2 . 3) nil (4 5 (6)) 7))
+=> (1 2 3 4 5 6 7)"
+ (let (elems)
+ (while (consp tree)
+ (let ((elem (pop tree)))
+ (while (consp elem)
+ (push (cdr elem) tree)
+ (setq elem (car elem)))
+ (if elem (push elem elems))))
+ (if tree (push tree elems))
+ (nreverse elems)))
+
+(compat-defun xor (cond1 cond2)
+ "Return the boolean exclusive-or of COND1 and COND2.
+If only one of the arguments is non-nil, return it; otherwise
+return nil."
+ (declare (pure t) (side-effect-free error-free))
+ (cond ((not cond1) cond2)
+ ((not cond2) cond1)))
+
+(compat-defvar regexp-unmatchable "\\`a\\`"
+ "Standard regexp guaranteed not to match any string at all."
+ :constant t)
+
+(compat-defun assoc-delete-all (key alist &optional test)
+ "Delete from ALIST all elements whose car is KEY.
+Compare keys with TEST. Defaults to `equal'.
+Return the modified alist.
+Elements of ALIST that are not conses are ignored."
+ :prefix t
+ (unless test (setq test #'equal))
+ (while (and (consp (car alist))
+ (funcall test (caar alist) key))
+ (setq alist (cdr alist)))
+ (let ((tail alist) tail-cdr)
+ (while (setq tail-cdr (cdr tail))
+ (if (and (consp (car tail-cdr))
+ (funcall test (caar tail-cdr) key))
+ (setcdr tail (cdr tail-cdr))
+ (setq tail tail-cdr))))
+ alist)
+
+;;;; Defined in simple.el
+
+;;* UNTESTED
+(compat-defun decoded-time-second (time)
+ "The seconds in TIME, which is a value returned by `decode-time'.
+This is an integer between 0 and 60 (inclusive). (60 is a leap
+second, which only some operating systems support.)"
+ (nth 0 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-minute (time)
+ "The minutes in TIME, which is a value returned by `decode-time'.
+This is an integer between 0 and 59 (inclusive)."
+ (nth 1 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-hour (time)
+ "The hours in TIME, which is a value returned by `decode-time'.
+This is an integer between 0 and 23 (inclusive)."
+ (nth 2 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-day (time)
+ "The day-of-the-month in TIME, which is a value returned by `decode-time'.
+This is an integer between 1 and 31 (inclusive)."
+ (nth 3 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-month (time)
+ "The month in TIME, which is a value returned by `decode-time'.
+This is an integer between 1 and 12 (inclusive). January is 1."
+ (nth 4 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-year (time)
+ "The year in TIME, which is a value returned by `decode-time'.
+This is a four digit integer."
+ (nth 5 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-weekday (time)
+ "The day-of-the-week in TIME, which is a value returned by `decode-time'.
+This is a number between 0 and 6, and 0 is Sunday."
+ (nth 6 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-dst (time)
+ "The daylight saving time in TIME, which is a value returned by `decode-time'.
+This is t if daylight saving time is in effect, and nil if not."
+ (nth 7 time))
+
+;;* UNTESTED
+(compat-defun decoded-time-zone (time)
+ "The time zone in TIME, which is a value returned by `decode-time'.
+This is an integer indicating the UTC offset in seconds, i.e.,
+the number of seconds east of Greenwich."
+ (nth 8 time))
+
+;; TODO define gv-setters
+
+;;;; Defined in files.el
+
+(compat-defun file-size-human-readable (file-size &optional flavor space unit)
+ "Handle the optional third and forth argument:
+
+Optional third argument SPACE is a string put between the number and unit.
+It defaults to the empty string. We recommend a single space or
+non-breaking space, unless other constraints prohibit a space in that
+position.
+
+Optional fourth argument UNIT is the unit to use. It defaults to \"B\"
+when FLAVOR is `iec' and the empty string otherwise. We recommend \"B\"
+in all cases, since that is the standard symbol for byte."
+ :prefix t
+ (let ((power (if (or (null flavor) (eq flavor 'iec))
+ 1024.0
+ 1000.0))
+ (prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y")))
+ (while (and (>= file-size power) (cdr prefixes))
+ (setq file-size (/ file-size power)
+ prefixes (cdr prefixes)))
+ (let* ((prefix (car prefixes))
+ (prefixed-unit (if (eq flavor 'iec)
+ (concat
+ (if (string= prefix "k") "K" prefix)
+ (if (string= prefix "") "" "i")
+ (or unit "B"))
+ (concat prefix unit))))
+ (format (if (and (>= (mod file-size 1.0) 0.05)
+ (< (mod file-size 1.0) 0.95))
+ "%.1f%s%s"
+ "%.0f%s%s")
+ file-size
+ (if (string= prefixed-unit "") "" (or space ""))
+ prefixed-unit))))
+
+(declare-function compat--file-name-quote "compat-26"
+ (name &optional top))
+
+;;*UNTESTED
+(compat-defun exec-path ()
+ "Return list of directories to search programs to run in remote subprocesses.
+The remote host is identified by `default-directory'. For remote
+hosts that do not support subprocesses, this returns nil.
+If `default-directory' is a local directory, this function returns
+the value of the variable `exec-path'."
+ :realname compat--exec-path
+ (cond
+ ((let ((handler (find-file-name-handler default-directory 'exec-path)))
+ ;; FIXME: The handler was added in 27.1, and this compatibility
+ ;; function only applies to versions of Emacs before that.
+ (when handler
+ (condition-case nil
+ (funcall handler 'exec-path)
+ (error nil)))))
+ ((file-remote-p default-directory)
+ ;; TODO: This is not completely portable, even if "sh" and
+ ;; "getconf" should be provided on every POSIX system, the chance
+ ;; of this not working are greater than zero.
+ ;;
+ ;; FIXME: This invokes a shell process every time exec-path is
+ ;; called. It should instead be cached on a host-local basis.
+ (with-temp-buffer
+ (if (condition-case nil
+ (zerop (process-file "sh" nil t nil "-c" "getconf PATH"))
+ (file-missing t))
+ (list "/bin" "/usr/bin")
+ (let (path)
+ (while (re-search-forward "\\([^:]+?\\)[\n:]" nil t)
+ (push (match-string 1) path))
+ (nreverse path)))))
+ (exec-path)))
+
+(declare-function compat--file-local-name "compat-26"
+ (file))
+
+;;*UNTESTED
+(compat-defun executable-find (command &optional remote)
+ "Search for COMMAND in `exec-path' and return the absolute file name.
+Return nil if COMMAND is not found anywhere in `exec-path'. If
+REMOTE is non-nil, search on the remote host indicated by
+`default-directory' instead."
+ :prefix t
+ (if (and remote (file-remote-p default-directory))
+ (let ((res (locate-file
+ command
+ (mapcar
+ (apply-partially
+ #'concat (file-remote-p default-directory))
+ (compat--exec-path))
+ exec-suffixes 'file-executable-p)))
+ (when (stringp res) (compat--file-local-name res)))
+ (executable-find command)))
+
+;; TODO provide advice for directory-files-recursively
+
+;;;; Defined in format-spec.el
+
+;; TODO provide advice for format-spec
+
+;;;; Defined in regexp-opt.el
+
+(compat-defun regexp-opt (strings &optional paren)
+ "Handle an empty list of strings."
+ :prefix t
+ (if (null strings)
+ (let ((re "\\`a\\`"))
+ (cond ((null paren)
+ (concat "\\(?:" re "\\)"))
+ ((stringp paren)
+ (concat paren re "\\)"))
+ ((eq paren 'words)
+ (concat "\\<\\(" re "\\)\\>"))
+ ((eq paren 'symbols)
+ (concat "\\_\\(<" re "\\)\\_>"))
+ ((concat "\\(" re "\\)"))))
+ (regexp-opt strings paren)))
+
+;;;; Defined in package.el
+
+(declare-function lm-header "lisp-mnt")
+
+;;* UNTESTED
+(compat-defun package-get-version ()
+ "Return the version number of the package in which this is used.
+Assumes it is used from an Elisp file placed inside the top-level directory
+of an installed ELPA package.
+The return value is a string (or nil in case we can’t find it)."
+ ;; In a sense, this is a lie, but it does just what we want: precompute
+ ;; the version at compile time and hardcodes it into the .elc file!
+ (declare (pure t))
+ ;; Hack alert!
+ (let ((file
+ (or (and (boundp 'byte-compile-current-file) byte-compile-current-file)
+ load-file-name
+ buffer-file-name)))
+ (cond
+ ((null file) nil)
+ ;; Packages are normally installed into directories named "<pkg>-<vers>",
+ ;; so get the version number from there.
+ ((string-match
+ "/[^/]+-\\([0-9]\\(?:[0-9.]\\|pre\\|beta\\|alpha\\|snapshot\\)+\\)/[^/]+\\'"
+ file)
+ (match-string 1 file))
+ ;; For packages run straight from the an elpa.git clone, there's no
+ ;; "-<vers>" in the directory name, so we have to fetch the version
+ ;; the hard way.
+ ((let* ((pkgdir (file-name-directory file))
+ (pkgname (file-name-nondirectory (directory-file-name pkgdir)))
+ (mainfile (expand-file-name (concat pkgname ".el") pkgdir)))
+ (when (file-readable-p mainfile)
+ (require 'lisp-mnt)
+ (with-temp-buffer
+ (insert-file-contents mainfile)
+ (or (lm-header "package-version")
+ (lm-header "version")))))))))
+
+
+;;;; Defined in dired.el
+
+(declare-function
+ dired-get-marked-files "dired.el"
+ (&optional localp arg filter distinguish-one-marked error))
+
+;;* UNTESTED
+(compat-defun dired-get-marked-files
+ (&optional localp arg filter distinguish-one-marked error)
+ "Return the marked files’ names as list of strings."
+ :feature 'dired
+ :prefix t
+ (let ((result (dired-get-marked-files localp arg filter distinguish-one-marked)))
+ (if (and (null result) error)
+ (user-error (if (stringp error) error "No files specified"))
+ result)))
+
+;;;; Defined in time-date.el
+
+(compat-defun date-days-in-month (year month)
+ "The number of days in MONTH in YEAR."
+ :feature 'time-date
+ (unless (and (numberp month)
+ (<= 1 month)
+ (<= month 12))
+ (error "Month %s is invalid" month))
+ (if (= month 2)
+ (if (date-leap-year-p year)
+ 29
+ 28)
+ (if (memq month '(1 3 5 7 8 10 12))
+ 31
+ 30)))
+
+(compat--inhibit-prefixed (provide 'compat-27))
+;;; compat-27.el ends here
--- /dev/null
+;;; compat-28.el --- Compatibility Layer for Emacs 28.1 -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; URL: https://git.sr.ht/~pkal/compat/
+;; Keywords: lisp
+
+;; 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:
+
+;; Find here the functionality added in Emacs 28.1, needed by older
+;; versions.
+;;
+;; Only load this library if you need to use one of the following
+;; functions:
+;;
+;; - `unlock-buffer'
+;; - `string-width'
+;; - `directory-files'
+;; - `json-serialize'
+;; - `json-insert'
+;; - `json-parse-string'
+;; - `json-parse-buffer'
+;; - `count-windows'
+
+;;; Code:
+
+(require 'compat-macs "compat-macs.el")
+
+(compat-declare-version "28.1")
+
+;;;; Defined in fns.c
+
+;;* INCOMPLETE FEATURE: Should handle multibyte regular expressions
+(compat-defun string-search (needle haystack &optional start-pos)
+ "Search for the string NEEDLE in the strign HAYSTACK.
+
+The return value is the position of the first occurrence of
+NEEDLE in HAYSTACK, or nil if no match was found.
+
+The optional START-POS argument says where to start searching in
+HAYSTACK and defaults to zero (start at the beginning).
+It must be between zero and the length of HAYSTACK, inclusive.
+
+Case is always significant and text properties are ignored."
+ :note "Prior to Emacs 27 `string-match' has issues handling
+multibyte regular expressions. As the compatibility function
+for `string-search' is implemented via `string-match', these
+issues are inherited."
+ (when (and start-pos (or (< (length haystack) start-pos)
+ (< start-pos 0)))
+ (signal 'args-out-of-range (list start-pos)))
+ (save-match-data
+ (let ((case-fold-search nil))
+ (string-match (regexp-quote needle) haystack start-pos))))
+
+(compat-defun length= (sequence length)
+ "Returns non-nil if SEQUENCE has a length equal to LENGTH."
+ (cond
+ ((null sequence) (zerop length))
+ ((consp sequence)
+ (and (null (nthcdr length sequence))
+ (nthcdr (1- length) sequence)
+ t))
+ ((arrayp sequence)
+ (= (length sequence) length))
+ ((signal 'wrong-type-argument sequence))))
+
+(compat-defun length< (sequence length)
+ "Returns non-nil if SEQUENCE is shorter than LENGTH."
+ (cond
+ ((null sequence) (not (zerop length)))
+ ((listp sequence)
+ (null (nthcdr (1- length) sequence)))
+ ((arrayp sequence)
+ (< (length sequence) length))
+ ((signal 'wrong-type-argument sequence))))
+
+(compat-defun length> (sequence length)
+ "Returns non-nil if SEQUENCE is longer than LENGTH."
+ (cond
+ ((listp sequence)
+ (and (nthcdr length sequence) t))
+ ((arrayp sequence)
+ (> (length sequence) length))
+ ((signal 'wrong-type-argument sequence))))
+
+;;;; Defined in fileio.c
+
+(compat-defun file-name-concat (directory &rest components)
+ "Append COMPONENTS to DIRECTORY and return the resulting string.
+Elements in COMPONENTS must be a string or nil.
+DIRECTORY or the non-final elements in COMPONENTS may or may not end
+with a slash -- if they don’t end with a slash, a slash will be
+inserted before contatenating."
+ (let ((seperator (eval-when-compile
+ (if (memq system-type '(ms-dos windows-nt cygwin))
+ "\\" "/")))
+ (last (if components (car (last components)) directory)))
+ (mapconcat (lambda (part)
+ (if (eq part last) ;the last component is not modified
+ last
+ (replace-regexp-in-string
+ (concat seperator "+\\'") "" part)))
+ (cons directory components)
+ seperator)))
+
+;;;; Defined in alloc.c
+
+;;* UNTESTED (but also not necessary)
+(compat-defun garbage-collect-maybe (_factor)
+ "Call ‘garbage-collect’ if enough allocation happened.
+FACTOR determines what \"enough\" means here: If FACTOR is a
+positive number N, it means to run GC if more than 1/Nth of the
+allocations needed to trigger automatic allocation took place.
+Therefore, as N gets higher, this is more likely to perform a GC.
+Returns non-nil if GC happened, and nil otherwise."
+ :note "For releases of Emacs before version 28, this function will do nothing."
+ ;; Do nothing
+ nil)
+
+;;;; Defined in filelock.c
+
+(compat-defun unlock-buffer ()
+ "Handle `file-error' conditions:
+
+Handles file system errors by calling ‘display-warning’ and
+continuing as if the error did not occur."
+ :prefix t
+ (condition-case error
+ (unlock-buffer)
+ (file-error
+ (display-warning
+ '(unlock-file)
+ (message "%s, ignored" (error-message-string error))
+ :warning))))
+
+;;;; Defined in characters.c
+
+(compat-defun string-width (string &optional from to)
+ "Handle optional arguments FROM and TO:
+
+Optional arguments FROM and TO specify the substring of STRING to
+consider, and are interpreted as in `substring'."
+ :prefix t
+ (let* ((len (length string))
+ (from (or from 0))
+ (to (or to len)))
+ (if (and (= from 0) (= to len))
+ (string-width string)
+ (string-width (substring string from to)))))
+
+;;;; Defined in dired.c
+
+;;* UNTESTED
+(compat-defun directory-files (directory &optional full match nosort count)
+ "Handle additional optional argument COUNT:
+
+If COUNT is non-nil and a natural number, the function will
+ return COUNT number of file names (if so many are present)."
+ :prefix t
+ (let ((files (directory-files directory full match nosort)))
+ (when (natnump count)
+ (setf (nthcdr count files) nil))
+ files))
+
+;;;; Defined in json.c
+
+(declare-function json-insert nil (object &rest args))
+(declare-function json-serialize nil (object &rest args))
+(declare-function json-parse-string nil (string &rest args))
+(declare-function json-parse-buffer nil (&rest args))
+
+(compat-defun json-serialize (object &rest args)
+ "Handle top-level JSON values."
+ :prefix t
+ :min-version "27"
+ (if (or (listp object) (vectorp object))
+ (apply #'json-serialize object args)
+ (substring (json-serialize (list object)) 1 -1)))
+
+(compat-defun json-insert (object &rest args)
+ "Handle top-level JSON values."
+ :prefix t
+ :min-version "27"
+ (if (or (listp object) (vectorp object))
+ (apply #'json-insert object args)
+ ;; `compat-json-serialize' is not sharp-quoted as the byte
+ ;; compiled doesn't always know that the function has been
+ ;; defined, but it will only be used in this function if the
+ ;; prefixed definition of `json-serialize' (see above) has also
+ ;; been defined.
+ (insert (apply 'compat-json-serialize object args))))
+
+(compat-defun json-parse-string (string &rest args)
+ "Handle top-level JSON values."
+ :prefix t
+ :min-version "27"
+ (if (string-match-p "\\`[[:space:]]*[[{]" string)
+ (apply #'json-parse-string string args)
+ ;; Wrap the string in an array, and extract the value back using
+ ;; `elt', to ensure that no matter what the value of `:array-type'
+ ;; is we can access the first element.
+ (elt (apply #'json-parse-string (concat "[" string "]") args) 0)))
+
+(compat-defun json-parse-buffer (&rest args)
+ "Handle top-level JSON values."
+ :prefix t
+ :min-version "27"
+ (if (looking-at-p "[[:space:]]*[[{]")
+ (apply #'json-parse-buffer args)
+ (catch 'escape
+ (atomic-change-group
+ (with-syntax-table
+ (let ((st (make-syntax-table)))
+ (modify-syntax-entry ?\" "\"" st)
+ (modify-syntax-entry ?. "_" st)
+ st)
+ (let ((inhibit-read-only t))
+ (save-excursion
+ (insert "[")
+ (forward-sexp 1)
+ (insert "]"))))
+ (throw 'escape (elt (apply #'json-parse-buffer args) 0))))))
+
+;;;; xfaces.c
+
+(compat-defun color-values-from-color-spec (spec)
+ "Parse color SPEC as a numeric color and return (RED GREEN BLUE).
+This function recognises the following formats for SPEC:
+
+ #RGB, where R, G and B are hex numbers of equal length, 1-4 digits each.
+ rgb:R/G/B, where R, G, and B are hex numbers, 1-4 digits each.
+ rgbi:R/G/B, where R, G and B are floating-point numbers in [0,1].
+
+If SPEC is not in one of the above forms, return nil.
+
+Each of the 3 integer members of the resulting list, RED, GREEN,
+and BLUE, is normalized to have its value in [0,65535]."
+ (let ((case-fold-search nil))
+ (save-match-data
+ (cond
+ ((string-match
+ ;; (rx bos "#"
+ ;; (or (: (group-n 1 (= 1 hex)) (group-n 2 (= 1 hex)) (group-n 3 (= 1 hex)))
+ ;; (: (group-n 1 (= 2 hex)) (group-n 2 (= 2 hex)) (group-n 3 (= 2 hex)))
+ ;; (: (group-n 1 (= 3 hex)) (group-n 2 (= 3 hex)) (group-n 3 (= 3 hex)))
+ ;; (: (group-n 1 (= 4 hex)) (group-n 2 (= 4 hex)) (group-n 3 (= 4 hex))))
+ ;; eos)
+ "\\`#\\(?:\\(?1:[[:xdigit:]]\\{1\\}\\)\\(?2:[[:xdigit:]]\\{1\\}\\)\\(?3:[[:xdigit:]]\\{1\\}\\)\\|\\(?1:[[:xdigit:]]\\{2\\}\\)\\(?2:[[:xdigit:]]\\{2\\}\\)\\(?3:[[:xdigit:]]\\{2\\}\\)\\|\\(?1:[[:xdigit:]]\\{3\\}\\)\\(?2:[[:xdigit:]]\\{3\\}\\)\\(?3:[[:xdigit:]]\\{3\\}\\)\\|\\(?1:[[:xdigit:]]\\{4\\}\\)\\(?2:[[:xdigit:]]\\{4\\}\\)\\(?3:[[:xdigit:]]\\{4\\}\\)\\)\\'"
+ spec)
+ (let ((max (1- (ash 1 (* (- (match-end 1) (match-beginning 1)) 4)))))
+ (list (/ (* (string-to-number (match-string 1 spec) 16) 65535) max)
+ (/ (* (string-to-number (match-string 2 spec) 16) 65535) max)
+ (/ (* (string-to-number (match-string 3 spec) 16) 65535) max))))
+ ((string-match
+ ;; (rx bos "rgb:"
+ ;; (group (** 1 4 hex)) "/"
+ ;; (group (** 1 4 hex)) "/"
+ ;; (group (** 1 4 hex))
+ ;; eos)
+ "\\`rgb:\\([[:xdigit:]]\\{1,4\\}\\)/\\([[:xdigit:]]\\{1,4\\}\\)/\\([[:xdigit:]]\\{1,4\\}\\)\\'"
+ spec)
+ (list (/ (* (string-to-number (match-string 1 spec) 16) 65535)
+ (1- (ash 1 (* (- (match-end 1) (match-beginning 1)) 4))))
+ (/ (* (string-to-number (match-string 2 spec) 16) 65535)
+ (1- (ash 1 (* (- (match-end 2) (match-beginning 2)) 4))))
+ (/ (* (string-to-number (match-string 3 spec) 16) 65535)
+ (1- (ash 1 (* (- (match-end 3) (match-beginning 3)) 4))))))
+ ;; The "RGBi" (RGB Intensity) specification is defined by
+ ;; XCMS[0], see [1] for the implementation in Xlib.
+ ;;
+ ;; [0] http://www.nic.funet.fi/pub/X11/X11R4/DOCS/color/Xcms.text
+ ;; [1] https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/master/src/xcms/LRGB.c#L1392
+ ((string-match
+ ;; (rx bos "rgbi:" (* space)
+ ;; (group (? (or "-" "+"))
+ ;; (or (: (+ digit) (? "." (* digit)))
+ ;; (: "." (+ digit)))
+ ;; (? "e" (? (or "-" "+")) (+ digit)))
+ ;; "/" (* space)
+ ;; (group (? (or "-" "+"))
+ ;; (or (: (+ digit) (? "." (* digit)))
+ ;; (: "." (+ digit)))
+ ;; (? "e" (? (or "-" "+")) (+ digit)))
+ ;; "/" (* space)
+ ;; (group (? (or "-" "+"))
+ ;; (or (: (+ digit) (? "." (* digit)))
+ ;; (: "." (+ digit)))
+ ;; (? "e" (? (or "-" "+")) (+ digit)))
+ ;; eos)
+ "\\`rgbi:[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)/[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)/[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)\\'"
+ spec)
+ (let ((r (round (* (string-to-number (match-string 1 spec)) 65535)))
+ (g (round (* (string-to-number (match-string 2 spec)) 65535)))
+ (b (round (* (string-to-number (match-string 3 spec)) 65535))))
+ (when (and (<= 0 r) (<= r 65535)
+ (<= 0 g) (<= g 65535)
+ (<= 0 b) (<= b 65535))
+ (list r g b))))))))
+
+;;;; Defined in subr.el
+
+;;* INCOMPLETE FEATURE: Should handle multibyte regular expressions
+(compat-defun string-replace (fromstring tostring instring)
+ "Replace FROMSTRING with TOSTRING in INSTRING each time it occurs."
+ (when (equal fromstring "")
+ (signal 'wrong-length-argument '(0)))
+ (let ((case-fold-search nil))
+ (replace-regexp-in-string
+ (regexp-quote fromstring)
+ tostring instring
+ t t)))
+
+(compat-defun always (&rest _arguments)
+ "Do nothing and return t.
+This function accepts any number of ARGUMENTS, but ignores them.
+Also see `ignore'."
+ t)
+
+;;* UNTESTED
+(compat-defun insert-into-buffer (buffer &optional start end)
+ "Insert the contents of the current buffer into BUFFER.
+If START/END, only insert that region from the current buffer.
+Point in BUFFER will be placed after the inserted text."
+ (let ((current (current-buffer)))
+ (with-current-buffer buffer
+ (insert-buffer-substring current start end))))
+
+;;* UNTESTED
+(compat-defun replace-string-in-region (string replacement &optional start end)
+ "Replace STRING with REPLACEMENT in the region from START to END.
+The number of replaced occurrences are returned, or nil if STRING
+doesn't exist in the region.
+
+If START is nil, use the current point. If END is nil, use `point-max'.
+
+Comparisons and replacements are done with fixed case."
+ (if start
+ (when (< start (point-min))
+ (error "Start before start of buffer"))
+ (setq start (point)))
+ (if end
+ (when (> end (point-max))
+ (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))))
+
+;;* UNTESTED
+(compat-defun replace-regexp-in-region (regexp replacement &optional start end)
+ "Replace REGEXP with REPLACEMENT in the region from START to END.
+The number of replaced occurrences are returned, or nil if REGEXP
+doesn't exist in the region.
+
+If START is nil, use the current point. If END is nil, use `point-max'.
+
+Comparisons and replacements are done with fixed case.
+
+REPLACEMENT can use the following special elements:
+
+ `\\&' in NEWTEXT means substitute original matched text.
+ `\\N' means substitute what matched the Nth `\\(...\\)'.
+ If Nth parens didn't match, substitute nothing.
+ `\\\\' means insert one `\\'.
+ `\\?' is treated literally."
+ (if start
+ (when (< start (point-min))
+ (error "Start before start of buffer"))
+ (setq start (point)))
+ (if end
+ (when (> end (point-max))
+ (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))))
+
+;;* UNTESTED
+(compat-defun buffer-local-boundp (symbol buffer)
+ "Return non-nil if SYMBOL is bound in BUFFER.
+Also see `local-variable-p'."
+ (catch 'fail
+ (condition-case nil
+ (buffer-local-value symbol buffer)
+ (void-variable nil (throw 'fail nil)))
+ t))
+
+;;* UNTESTED
+(compat-defmacro with-existing-directory (&rest body)
+ "Execute BODY with `default-directory' bound to an existing directory.
+If `default-directory' is already an existing directory, it's not changed."
+ (declare (indent 0) (debug t))
+ (let ((quit (make-symbol "with-existing-directory-quit")))
+ `(catch ',quit
+ (dolist (dir (list default-directory
+ (expand-file-name "~/")
+ (getenv "TMPDIR")
+ "/tmp/"
+ ;; XXX: check if "/" works on non-POSIX
+ ;; system.
+ "/"))
+ (when (and dir (file-exists-p dir))
+ (throw ',quit (let ((default-directory dir))
+ ,@body)))))))
+
+;;* UNTESTED
+(compat-defmacro dlet (binders &rest body)
+ "Like `let' but using dynamic scoping."
+ (declare (indent 1) (debug let))
+ `(let (_)
+ ,@(mapcar (lambda (binder)
+ `(defvar ,(if (consp binder) (car binder) binder)))
+ binders)
+ (let ,binders ,@body)))
+
+(compat-defun ensure-list (object)
+ "Return OBJECT as a list.
+If OBJECT is already a list, return OBJECT itself. If it's
+not a list, return a one-element list containing OBJECT."
+ (if (listp object)
+ object
+ (list object)))
+
+(compat-defun subr-primitive-p (object)
+ "Return t if OBJECT is a built-in primitive function."
+ (subrp object))
+
+;;;; Defined in subr-x.el
+
+(compat-defun string-clean-whitespace (string)
+ "Clean up whitespace in STRING.
+All sequences of whitespaces in STRING are collapsed into a
+single space character, and leading/trailing whitespace is
+removed."
+ :feature 'subr-x
+ (let ((blank "[[:blank:]\r\n]+"))
+ (replace-regexp-in-string
+ "^[[:blank:]\r\n]+\\|[[:blank:]\r\n]+$"
+ ""
+ (replace-regexp-in-string
+ blank " " string))))
+
+(compat-defun string-fill (string length)
+ "Clean up whitespace in STRING.
+All sequences of whitespaces in STRING are collapsed into a
+single space character, and leading/trailing whitespace is
+removed."
+ :feature 'subr-x
+ (with-temp-buffer
+ (insert string)
+ (goto-char (point-min))
+ (let ((fill-column length)
+ (adaptive-fill-mode nil))
+ (fill-region (point-min) (point-max)))
+ (buffer-string)))
+
+(compat-defun string-lines (string &optional omit-nulls)
+ "Split STRING into a list of lines.
+If OMIT-NULLS, empty lines will be removed from the results."
+ :feature 'subr-x
+ (split-string string "\n" omit-nulls))
+
+(compat-defun string-pad (string length &optional padding start)
+ "Pad STRING to LENGTH using PADDING.
+If PADDING is nil, the space character is used. If not nil, it
+should be a character.
+
+If STRING is longer than the absolute value of LENGTH, no padding
+is done.
+
+If START is nil (or not present), the padding is done to the end
+of the string, and if non-nil, padding is done to the start of
+the string."
+ :feature 'subr-x
+ (unless (natnump length)
+ (signal 'wrong-type-argument (list 'natnump length)))
+ (let ((pad-length (- length (length string))))
+ (if (< pad-length 0)
+ string
+ (concat (and start
+ (make-string pad-length (or padding ?\s)))
+ string
+ (and (not start)
+ (make-string pad-length (or padding ?\s)))))))
+
+(compat-defun string-chop-newline (string)
+ "Remove the final newline (if any) from STRING."
+ :feature 'subr-x
+ (if (and (>= (length string) 1) (= (aref string (1- (length string))) ?\n))
+ (substring string 0 -1)
+ string))
+
+(compat-defmacro named-let (name bindings &rest body)
+ "Looping construct taken from Scheme.
+Like `let', bind variables in BINDINGS and then evaluate BODY,
+but with the twist that BODY can evaluate itself recursively by
+calling NAME, where the arguments passed to NAME are used
+as the new values of the bound variables in the recursive invocation."
+ :feature 'subr-x
+ (declare (indent 2) (debug (symbolp (&rest (symbolp form)) body)))
+ (let ((fargs (mapcar (lambda (b)
+ (let ((var (if (consp b) (car b) b)))
+ (make-symbol (symbol-name var))))
+ bindings))
+ (aargs (mapcar (lambda (b) (if (consp b) (cadr b))) bindings))
+ rargs)
+ (dotimes (i (length bindings))
+ (let ((b (nth i bindings)))
+ (push (list (if (consp b) (car b) b) (nth i fargs))
+ rargs)
+ (setf (if (consp b) (car b) b)
+ (nth i fargs))))
+ (letrec
+ ((quit (make-symbol "quit")) (self (make-symbol "self"))
+ (total-tco t)
+ (macro (lambda (&rest args)
+ (setq total-tco nil)
+ `(funcall ,self . ,args)))
+ ;; Based on `cl--self-tco':
+ (tco-progn (lambda (exprs)
+ (append
+ (butlast exprs)
+ (list (funcall tco (car (last exprs)))))))
+ (tco (lambda (expr)
+ (cond
+ ((eq (car-safe expr) 'if)
+ (append (list 'if
+ (cadr expr)
+ (funcall tco (nth 2 expr)))
+ (funcall tco-progn (nthcdr 3 expr))))
+ ((eq (car-safe expr) 'cond)
+ (let ((conds (cdr expr)) body)
+ (while conds
+ (let ((branch (pop conds)))
+ (push (cond
+ ((cdr branch) ;has tail
+ (funcall tco-progn branch))
+ ((null conds) ;last element
+ (list t (funcall tco (car branch))))
+ ((progn
+ branch)))
+ body)))
+ (cons 'cond (nreverse body))))
+ ((eq (car-safe expr) 'or)
+ (if (cddr expr)
+ (let ((var (make-symbol "var")))
+ `(let ((,var ,(cadr expr)))
+ (if ,var ,(funcall tco var)
+ ,(funcall tco (cons 'or (cddr expr))))))
+ (funcall tco (cadr expr))))
+ ((eq (car-safe expr) 'condition-case)
+ (append (list 'condition-case (cadr expr) (nth 2 expr))
+ (mapcar
+ (lambda (handler)
+ (cons (car handler)
+ (funcall tco-progn (cdr handler))))
+ (nthcdr 3 expr))))
+ ((memq (car-safe expr) '(and progn))
+ (cons (car expr) (funcall tco-progn (cdr expr))))
+ ((memq (car-safe expr) '(let let*))
+ (append (list (car expr) (cadr expr))
+ (funcall tco-progn (cddr expr))))
+ ((eq (car-safe expr) name)
+ (let (sets (args (cdr expr)))
+ (dolist (farg fargs)
+ (push (list farg (pop args))
+ sets))
+ (cons 'setq (apply #'nconc (nreverse sets)))))
+ (`(throw ',quit ,expr))))))
+ (let ((tco-body (funcall tco (macroexpand-all (macroexp-progn body)))))
+ (when tco-body
+ (setq body `((catch ',quit
+ (while t (let ,rargs ,@(macroexp-unprogn tco-body))))))))
+ (let ((expand (macroexpand-all (macroexp-progn body) (list (cons name macro)))))
+ (if total-tco
+ `(let ,bindings ,expand)
+ `(funcall
+ (letrec ((,self (lambda ,fargs ,expand))) ,self)
+ ,@aargs))))))
+
+;;;; Defined in files.el
+
+(declare-function compat--string-trim-left "compat-26" (string &optional regexp))
+(declare-function compat--directory-name-p "compat-25" (name))
+(compat-defun file-name-with-extension (filename extension)
+ "Set the EXTENSION of a FILENAME.
+The extension (in a file name) is the part that begins with the last \".\".
+
+Trims a leading dot from the EXTENSION so that either \"foo\" or
+\".foo\" can be given.
+
+Errors if the FILENAME or EXTENSION are empty, or if the given
+FILENAME has the format of a directory.
+
+See also `file-name-sans-extension'."
+ (let ((extn (compat--string-trim-left extension "[.]")))
+ (cond
+ ((string= filename "")
+ (error "Empty filename"))
+ ((string= extn "")
+ (error "Malformed extension: %s" extension))
+ ((compat--directory-name-p filename)
+ (error "Filename is a directory: %s" filename))
+ (t
+ (concat (file-name-sans-extension filename) "." extn)))))
+
+;;* UNTESTED
+(compat-defun directory-empty-p (dir)
+ "Return t if DIR names an existing directory containing no other files.
+Return nil if DIR does not name a directory, or if there was
+trouble determining whether DIR is a directory or empty.
+
+Symbolic links to directories count as directories.
+See `file-symlink-p' to distinguish symlinks."
+ (and (file-directory-p dir)
+ (null (directory-files dir nil directory-files-no-dot-files-regexp t))))
+
+(compat-defun file-modes-number-to-symbolic (mode &optional filetype)
+ "Return a string describing a file's MODE.
+For instance, if MODE is #o700, then it produces `-rwx------'.
+FILETYPE if provided should be a character denoting the type of file,
+such as `?d' for a directory, or `?l' for a symbolic link and will override
+the leading `-' char."
+ (string
+ (or filetype
+ (pcase (lsh mode -12)
+ ;; POSIX specifies that the file type is included in st_mode
+ ;; and provides names for the file types but values only for
+ ;; the permissions (e.g., S_IWOTH=2).
+
+ ;; (#o017 ??) ;; #define S_IFMT 00170000
+ (#o014 ?s) ;; #define S_IFSOCK 0140000
+ (#o012 ?l) ;; #define S_IFLNK 0120000
+ ;; (8 ??) ;; #define S_IFREG 0100000
+ (#o006 ?b) ;; #define S_IFBLK 0060000
+ (#o004 ?d) ;; #define S_IFDIR 0040000
+ (#o002 ?c) ;; #define S_IFCHR 0020000
+ (#o001 ?p) ;; #define S_IFIFO 0010000
+ (_ ?-)))
+ (if (zerop (logand 256 mode)) ?- ?r)
+ (if (zerop (logand 128 mode)) ?- ?w)
+ (if (zerop (logand 64 mode))
+ (if (zerop (logand 2048 mode)) ?- ?S)
+ (if (zerop (logand 2048 mode)) ?x ?s))
+ (if (zerop (logand 32 mode)) ?- ?r)
+ (if (zerop (logand 16 mode)) ?- ?w)
+ (if (zerop (logand 8 mode))
+ (if (zerop (logand 1024 mode)) ?- ?S)
+ (if (zerop (logand 1024 mode)) ?x ?s))
+ (if (zerop (logand 4 mode)) ?- ?r)
+ (if (zerop (logand 2 mode)) ?- ?w)
+ (if (zerop (logand 512 mode))
+ (if (zerop (logand 1 mode)) ?- ?x)
+ (if (zerop (logand 1 mode)) ?T ?t))))
+
+;;* UNTESTED
+(compat-defun file-backup-file-names (filename)
+ "Return a list of backup files for FILENAME.
+The list will be sorted by modification time so that the most
+recent files are first."
+ ;; `make-backup-file-name' will get us the right directory for
+ ;; ordinary or numeric backups. It might create a directory for
+ ;; backups as a side-effect, according to `backup-directory-alist'.
+ (let* ((filename (file-name-sans-versions
+ (make-backup-file-name (expand-file-name filename))))
+ (dir (file-name-directory filename))
+ files)
+ (dolist (file (file-name-all-completions
+ (file-name-nondirectory filename) dir))
+ (let ((candidate (concat dir file)))
+ (when (and (backup-file-name-p candidate)
+ (string= (file-name-sans-versions candidate) filename))
+ (push candidate files))))
+ (sort files #'file-newer-than-file-p)))
+
+(compat-defun make-lock-file-name (filename)
+ "Make a lock file name for FILENAME.
+This prepends \".#\" to the non-directory part of FILENAME, and
+doesn't respect `lock-file-name-transforms', as Emacs 28.1 and
+onwards does."
+ (expand-file-name
+ (concat
+ ".#" (file-name-nondirectory filename))
+ (file-name-directory filename)))
+
+;;;; Defined in files-x.el
+
+(declare-function tramp-tramp-file-p "tramp" (name))
+
+;;* UNTESTED
+(compat-defun null-device ()
+ "Return the best guess for the null device."
+ (require 'tramp)
+ (if (tramp-tramp-file-p default-directory)
+ "/dev/null"
+ null-device))
+
+;;;; Defined in minibuffer.el
+
+(compat-defun format-prompt (prompt default &rest format-args)
+ "Format PROMPT with DEFAULT.
+If FORMAT-ARGS is nil, PROMPT is used as a plain string. If
+FORMAT-ARGS is non-nil, PROMPT is used as a format control
+string, and FORMAT-ARGS are the arguments to be substituted into
+it. See `format' for details.
+
+If DEFAULT is a list, the first element is used as the default.
+If not, the element is used as is.
+
+If DEFAULT is nil or an empty string, no \"default value\" string
+is included in the return value."
+ (concat
+ (if (null format-args)
+ prompt
+ (apply #'format prompt format-args))
+ (and default
+ (or (not (stringp default))
+ (> (length default) 0))
+ (format " (default %s)"
+ (if (consp default)
+ (car default)
+ default)))
+ ": "))
+
+;;;; Defined in windows.el
+
+;;* UNTESTED
+(compat-defun count-windows (&optional minibuf all-frames)
+ "Handle optional argument ALL-FRAMES:
+
+If ALL-FRAMES is non-nil, count the windows in all frames instead
+just the selected frame."
+ :prefix t
+ (if all-frames
+ (let ((sum 0))
+ (dolist (frame (frame-list))
+ (with-selected-frame frame
+ (setq sum (+ (count-windows minibuf) sum))))
+ sum)
+ (count-windows minibuf)))
+
+;;;; Defined in thingatpt.el
+
+(declare-function mouse-set-point "mouse" (event &optional promote-to-region))
+
+;;* UNTESTED
+(compat-defun thing-at-mouse (event thing &optional no-properties)
+ "Return the THING at mouse click.
+Like `thing-at-point', but tries to use the event
+where the mouse button is clicked to find a thing nearby."
+ :feature 'thingatpt
+ (save-excursion
+ (mouse-set-point event)
+ (thing-at-point thing no-properties)))
+
+;;;; Defined in macroexp.el
+
+;;* UNTESTED
+(compat-defun macroexp-file-name ()
+ "Return the name of the file from which the code comes.
+Returns nil when we do not know.
+A non-nil result is expected to be reliable when called from a macro in order
+to find the file in which the macro's call was found, and it should be
+reliable as well when used at the top-level of a file.
+Other uses risk returning non-nil value that point to the wrong file."
+ :feature 'macroexp
+ (let ((file (car (last current-load-list))))
+ (or (if (stringp file) file)
+ (bound-and-true-p byte-compile-current-file))))
+
+;;;; Defined in env.el
+
+;;* UNTESTED
+(compat-defmacro with-environment-variables (variables &rest body)
+ "Set VARIABLES in the environent and execute BODY.
+VARIABLES is a list of variable settings of the form (VAR VALUE),
+where VAR is the name of the variable (a string) and VALUE
+is its value (also a string).
+
+The previous values will be be restored upon exit."
+ (declare (indent 1) (debug (sexp body)))
+ (unless (consp variables)
+ (error "Invalid VARIABLES: %s" variables))
+ `(let ((process-environment (copy-sequence process-environment)))
+ ,@(mapcar (lambda (elem)
+ `(setenv ,(car elem) ,(cadr elem)))
+ variables)
+ ,@body))
+
+;;;; Defined in button.el
+
+;;* UNTESTED
+(compat-defun button-buttonize (string callback &optional data)
+ "Make STRING into a button and return it.
+When clicked, CALLBACK will be called with the DATA as the
+function argument. If DATA isn't present (or is nil), the button
+itself will be used instead as the function argument."
+ :feature 'button
+ (propertize string
+ 'face 'button
+ 'button t
+ 'follow-link t
+ 'category t
+ 'button-data data
+ 'keymap button-map
+ 'action callback))
+
+;;;; Defined in autoload.el
+
+(defvar generated-autoload-file)
+
+;;* UNTESTED
+(compat-defun make-directory-autoloads (dir output-file)
+ "Update autoload definitions for Lisp files in the directories DIRS.
+DIR can be either a single directory or a list of
+directories. (The latter usage is discouraged.)
+
+The autoloads will be written to OUTPUT-FILE. If any Lisp file
+binds `generated-autoload-file' as a file-local variable, write
+its autoloads into the specified file instead.
+
+The function does NOT recursively descend into subdirectories of the
+directory or directories specified."
+ (let ((generated-autoload-file output-file))
+ ;; We intentionally don't sharp-quote
+ ;; `update-directory-autoloads', because it was deprecated in
+ ;; Emacs 28 and we don't want to trigger the byte compiler for
+ ;; newer versions.
+ (apply 'update-directory-autoloads
+ (if (listp dir) dir (list dir)))))
+
+;;;; Defined in time-data.el
+
+(compat-defun decoded-time-period (time)
+ "Interpret DECODED as a period and return its length in seconds.
+For computational purposes, years are 365 days long and months
+are 30 days long."
+ :feature 'time-date
+ :version "28"
+ ;; Inlining the definitions from compat-27
+ (+ (if (consp (nth 0 time))
+ ;; Fractional second.
+ (/ (float (car (nth 0 time)))
+ (cdr (nth 0 time)))
+ (or (nth 0 time) 0))
+ (* (or (nth 1 time) 0) 60)
+ (* (or (nth 2 time) 0) 60 60)
+ (* (or (nth 3 time) 0) 60 60 24)
+ (* (or (nth 4 time) 0) 60 60 24 30)
+ (* (or (nth 5 time) 0) 60 60 24 365)))
+
+(compat--inhibit-prefixed (provide 'compat-28))
+;;; compat-28.el ends here
--- /dev/null
+;;; compat-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
+;;
+;;; Code:
+
+(add-to-list 'load-path (directory-file-name
+ (or (file-name-directory #$) (car load-path))))
+
+\f
+;;;### (autoloads nil "compat-27" "compat-27.el" (0 0 0 0))
+;;; Generated autoloads from compat-27.el
+
+(register-definition-prefixes "compat-27" '("derived-mode-p"))
+
+;;;***
+\f
+;;;### (autoloads nil "compat-help" "compat-help.el" (0 0 0 0))
+;;; Generated autoloads from compat-help.el
+
+(register-definition-prefixes "compat-help" '("compat---describe"))
+
+;;;***
+\f
+;;;### (autoloads nil "compat-macs" "compat-macs.el" (0 0 0 0))
+;;; Generated autoloads from compat-macs.el
+
+(register-definition-prefixes "compat-macs" '("compat-"))
+
+;;;***
+\f
+;;;### (autoloads nil nil ("compat-24.el" "compat-25.el" "compat-26.el"
+;;;;;; "compat-28.el" "compat-font-lock.el" "compat-pkg.el" "compat.el")
+;;;;;; (0 0 0 0))
+
+;;;***
+\f
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; coding: utf-8
+;; End:
+;;; compat-autoloads.el ends here
--- /dev/null
+;;; compat-font-lock.el --- -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Keywords:
+
+;; 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:
+
+;; Optional font-locking for `compat' definitions. Every symbol with
+;; an active compatibility definition will be highlighted.
+;;
+;; Load this file to enable the functionality.
+
+;;; Code:
+
+(eval-and-compile
+ (require 'cl-lib)
+ (require 'compat-macs))
+
+(defvar compat-generate-common-fn)
+(let ((compat-generate-common-fn
+ (lambda (name _def-fn _install-fn check-fn attr _type)
+ (unless (and (plist-get attr :no-highlight)
+ (funcall check-fn))
+ `(font-lock-add-keywords
+ 'emacs-lisp-mode
+ ',`((,(concat "\\_<\\("
+ (regexp-quote (symbol-name name))
+ "\\)\\_>")
+ 1 font-lock-preprocessor-face prepend)))))))
+ (load "compat"))
+
+(provide 'compat-font-lock)
+;;; compat-font-lock.el ends here
--- /dev/null
+;;; compat-help.el --- Documentation for compat functions -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+
+;; 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:
+
+;; Load this file to insert `compat'-relevant documentation next to
+;; the regular documentation of a symbol.
+
+;;; Code:
+
+(defun compat---describe (symbol)
+ "Insert documentation for SYMBOL if it has compatibility code."
+ (let ((compat (get symbol 'compat-def)))
+ (when compat
+ (let ((doc (get compat 'compat-doc))
+ (start (point)))
+ (when doc
+ (insert "There is a ")
+ (insert-button
+ "compatibility notice"
+ 'action (let ((type (get compat 'compat-type)))
+ (cond
+ ((memq type '(func macro advice))
+ #'find-function)
+ ((memq type '(variable))
+ #'find-variable)
+ ((error "Unknown type"))))
+ 'button-data compat)
+ (insert (format " for %s (for versions of Emacs before %s):"
+ (symbol-name symbol)
+ (get compat 'compat-version)))
+ (add-text-properties start (point) '(face bold))
+ (newline 2)
+ (insert (substitute-command-keys doc))
+ (fill-region start (point))
+ (newline 2))))))
+
+(add-hook 'help-fns-describe-function-functions #'compat---describe)
+
+(provide 'compat-help)
+;;; compat-help.el ends here
--- /dev/null
+;;; compat-macs.el --- Compatibility Macros -*- lexical-binding: t; no-byte-compile: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Keywords: lisp
+
+;; 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:
+
+;; These macros are used to define compatibility functions, macros and
+;; advice.
+
+;;; Code:
+
+(defmacro compat--ignore (&rest _)
+ "Ignore all arguments."
+ nil)
+
+(defvar compat--inhibit-prefixed nil
+ "Non-nil means that prefixed definitions are not loaded.
+A prefixed function is something like `compat-assoc', that is
+only made visible when the respective compatibility version file
+is loaded (in this case `compat-26').")
+
+(defmacro compat--inhibit-prefixed (&rest body)
+ "Ignore BODY unless `compat--inhibit-prefixed' is true."
+ `(unless (bound-and-true-p compat--inhibit-prefixed)
+ ,@body))
+
+(defvar compat-current-version nil
+ "Default version to use when no explicit version was given.")
+
+(defmacro compat-declare-version (version)
+ "Set the Emacs version that is currently being handled to VERSION."
+ ;; FIXME: Avoid setting the version for any definition that might
+ ;; follow, but try to restrict it to the current file/buffer.
+ (setq compat-current-version version)
+ nil)
+
+(defvar compat--generate-function #'compat--generate-default
+ "Function used to generate compatibility code.
+The function must take six arguments: NAME, DEF-FN, INSTALL-FN,
+CHECK-FN, ATTR and TYPE. The resulting body is constructed by
+invoking the functions DEF-FN (passed the \"realname\" and the
+version number, returning the compatibility definition), the
+INSTALL-FN (passed the \"realname\" and returning the
+installation code), CHECK-FN (passed the \"realname\" and
+returning a check to see if the compatibility definition should
+be installed). ATTR is a plist used to modify the generated
+code. The following attributes are handled, all others are
+ignored:
+
+- :min-version :: Prevent the compatibility definition from begin
+ installed in versions older than indicated (string).
+
+- :max-version :: Prevent the compatibility definition from begin
+ installed in versions newer than indicated (string).
+
+- :feature :: The library the code is supposed to be loaded
+ with (via `eval-after-load').
+
+- :cond :: Only install the compatibility code, iff the value
+ evaluates to non-nil.
+
+ For prefixed functions, this can be interpreted as a test to
+ `defalias' an existing definition or not.
+
+- :no-highlight :: Do not highlight this definition as
+ compatibility function.
+
+- :version :: Manual specification of the version the compatee
+ code was defined in (string).
+
+- :realname :: Manual specification of a \"realname\" to use for
+ the compatibility definition (symbol).
+
+- :notes :: Additional notes that a developer using this
+ compatibility function should keep in mind.
+
+- :prefix :: Add a `compat-' prefix to the name, and define the
+ compatibility code unconditionally.
+
+TYPE is used to set the symbol property `compat-type' for NAME.")
+
+(defun compat--generate-default (name def-fn install-fn check-fn attr type)
+ "Generate a leaner compatibility definition.
+See `compat-generate-function' for details on the arguments NAME,
+DEF-FN, INSTALL-FN, CHECK-FN, ATTR and TYPE."
+ (let* ((min-version (plist-get attr :min-version))
+ (max-version (plist-get attr :max-version))
+ (feature (plist-get attr :feature))
+ (cond (plist-get attr :cond))
+ (version (or (plist-get attr :version)
+ compat-current-version))
+ (realname (or (plist-get attr :realname)
+ (intern (format "compat--%S" name))))
+ (check (cond
+ ((or (and min-version
+ (version< emacs-version min-version))
+ (and max-version
+ (version< max-version emacs-version)))
+ '(compat--ignore))
+ ((plist-get attr :prefix)
+ '(compat--inhibit-prefixed))
+ ((and version (version<= version emacs-version) (not cond))
+ '(compat--ignore))
+ (`(when (and ,(if cond cond t)
+ ,(funcall check-fn)))))))
+ (cond
+ ((and (plist-get attr :prefix) (memq type '(func macro))
+ (string-match "\\`compat-\\(.+\\)\\'" (symbol-name name))
+ (let* ((actual-name (intern (match-string 1 (symbol-name name))))
+ (body (funcall install-fn actual-name version)))
+ (when (and (version<= version emacs-version)
+ (fboundp actual-name))
+ `(,@check
+ ,(if feature
+ ;; See https://nullprogram.com/blog/2018/02/22/:
+ `(eval-after-load ,feature `(funcall ',(lambda () ,body)))
+ body))))))
+ ((plist-get attr :realname)
+ `(progn
+ ,(funcall def-fn realname version)
+ (,@check
+ ,(let ((body (funcall install-fn realname version)))
+ (if feature
+ ;; See https://nullprogram.com/blog/2018/02/22/:
+ `(eval-after-load ,feature `(funcall ',(lambda () ,body)))
+ body)))))
+ ((let* ((body (if (eq type 'advice)
+ `(,@check
+ ,(funcall def-fn realname version)
+ ,(funcall install-fn realname version))
+ `(,@check ,(funcall def-fn name version)))))
+ (if feature
+ ;; See https://nullprogram.com/blog/2018/02/22/:
+ `(eval-after-load ,feature `(funcall ',(lambda () ,body)))
+ body))))))
+
+(defun compat-generate-common (name def-fn install-fn check-fn attr type)
+ "Common code for generating compatibility definitions.
+See `compat-generate-function' for details on the arguments NAME,
+DEF-FN, INSTALL-FN, CHECK-FN, ATTR and TYPE."
+ (when (and (plist-get attr :cond) (plist-get attr :prefix))
+ (error "A prefixed function %s cannot have a condition" name))
+ (funcall compat--generate-function
+ name def-fn install-fn check-fn attr type))
+
+(defun compat-common-fdefine (type name arglist docstring rest)
+ "Generate compatibility code for a function NAME.
+TYPE is one of `func', for functions and `macro' for macros, and
+`advice' ARGLIST is passed on directly to the definition, and
+DOCSTRING is prepended with a compatibility note. REST contains
+the remaining definition, that may begin with a property list of
+attributes (see `compat-generate-common')."
+ (let ((oldname name) (body rest))
+ (while (keywordp (car body))
+ (setq body (cddr body)))
+ ;; It might be possible to set these properties otherwise. That
+ ;; should be looked into and implemented if it is the case.
+ (when (and (listp (car-safe body)) (eq (caar body) 'declare))
+ (when (version<= emacs-version "25")
+ (delq (assq 'side-effect-free (car body)) (car body))
+ (delq (assq 'pure (car body)) (car body))))
+ ;; Check if we want an explicitly prefixed function
+ (when (plist-get rest :prefix)
+ (setq name (intern (format "compat-%s" name))))
+ (compat-generate-common
+ name
+ (lambda (realname version)
+ `(,(cond
+ ((memq type '(func advice)) 'defun)
+ ((eq type 'macro) 'defmacro)
+ ((error "Unknown type")))
+ ,realname ,arglist
+ ;; Prepend compatibility notice to the actual
+ ;; documentation string.
+ ,(let ((type (cond
+ ((eq type 'func) "function")
+ ((eq type 'macro) "macro")
+ ((eq type 'advice) "advice")
+ ((error "Unknown type")))))
+ (if version
+ (format
+ "[Compatibility %s for `%S', defined in Emacs %s]\n\n%s"
+ type oldname version docstring)
+ (format
+ "[Compatibility %s for `%S']\n\n%s"
+ type oldname docstring)))
+ ;; Advice may use the implicit variable `oldfun', but
+ ;; to avoid triggering the byte compiler, we make
+ ;; sure the argument is used at least once.
+ ,@(if (eq type 'advice)
+ (cons '(ignore oldfun) body)
+ body)))
+ (lambda (realname _version)
+ (cond
+ ((memq type '(func macro))
+ ;; Functions and macros are installed by
+ ;; aliasing the name of the compatible
+ ;; function to the name of the compatibility
+ ;; function.
+ `(defalias ',name #',realname))
+ ((eq type 'advice)
+ `(advice-add ',name :around #',realname))))
+ (lambda ()
+ (cond
+ ((memq type '(func macro))
+ `(not (fboundp ',name)))
+ ((eq type 'advice) t)))
+ rest type)))
+
+(defmacro compat-defun (name arglist docstring &rest rest)
+ "Define NAME with arguments ARGLIST as a compatibility function.
+The function must be documented in DOCSTRING. REST may begin
+with a plist, that is interpreted by the macro but not passed on
+to the actual function. See `compat-generate-common' for a
+listing of attributes.
+
+The definition will only be installed, if the version this
+function was defined in, as indicated by the `:version'
+attribute, is greater than the current Emacs version."
+ (declare (debug (&define name (&rest symbolp)
+ stringp
+ [&rest keywordp sexp]
+ def-body))
+ (doc-string 3) (indent 2))
+ (compat-common-fdefine 'func name arglist docstring rest))
+
+(defmacro compat-defmacro (name arglist docstring &rest rest)
+ "Define NAME with arguments ARGLIST as a compatibility macro.
+The macro must be documented in DOCSTRING. REST may begin
+with a plist, that is interpreted by this macro but not passed on
+to the actual macro. See `compat-generate-common' for a
+listing of attributes.
+
+The definition will only be installed, if the version this
+function was defined in, as indicated by the `:version'
+attribute, is greater than the current Emacs version."
+ (declare (debug compat-defun) (doc-string 3) (indent 2))
+ (compat-common-fdefine 'macro name arglist docstring rest))
+
+(defmacro compat-advise (name arglist docstring &rest rest)
+ "Define NAME with arguments ARGLIST as a compatibility advice.
+The advice function must be documented in DOCSTRING. REST may
+begin with a plist, that is interpreted by this macro but not
+passed on to the actual advice function. See
+`compat-generate-common' for a listing of attributes. The advice
+wraps the old definition, that is accessible via using the symbol
+`oldfun'.
+
+The advice will only be installed, if the version this function
+was defined in, as indicated by the `:version' attribute, is
+greater than the current Emacs version."
+ (declare (debug compat-defun) (doc-string 3) (indent 2))
+ (compat-common-fdefine 'advice name (cons 'oldfun arglist) docstring rest))
+
+(defmacro compat-defvar (name initval docstring &rest attr)
+ "Declare compatibility variable NAME with initial value INITVAL.
+The obligatory documentation string DOCSTRING must be given.
+
+The remaining arguments ATTR form a plist, modifying the
+behaviour of this macro. See `compat-generate-common' for a
+listing of attributes. Furthermore, `compat-defvar' also handles
+the attribute `:local' that either makes the variable permanent
+local with a value of `permanent' or just buffer local with any
+non-nil value."
+ (declare (debug (name form stringp [&rest keywordp sexp]))
+ (doc-string 3) (indent 2))
+ ;; Check if we want an explicitly prefixed function
+ (let ((oldname name))
+ (when (plist-get attr :prefix)
+ (setq name (intern (format "compat-%s" name))))
+ (compat-generate-common
+ name
+ (lambda (realname version)
+ (let ((localp (plist-get attr :local)))
+ `(progn
+ (,(if (plist-get attr :constant) 'defconst 'defvar)
+ ,realname ,initval
+ ;; Prepend compatibility notice to the actual
+ ;; documentation string.
+ ,(if version
+ (format
+ "[Compatibility variable for `%S', defined in Emacs %s]\n\n%s"
+ oldname version docstring)
+ (format
+ "[Compatibility variable for `%S']\n\n%s"
+ oldname docstring)))
+ ;; Make variable as local if necessary
+ ,(cond
+ ((eq localp 'permanent)
+ `(put ',realname 'permanent-local t))
+ (localp
+ `(make-variable-buffer-local ',realname))))))
+ (lambda (realname _version)
+ `(defvaralias ',name ',realname))
+ (lambda ()
+ `(not (boundp ',name)))
+ attr 'variable)))
+
+(provide 'compat-macs)
+;;; compat-macs.el ends here
--- /dev/null
+;; Generated package description from compat.el -*- no-byte-compile: t -*-
+(define-package "compat" "28.1.2.2" "Emacs Lisp Compatibility Library" '((emacs "24.3") (nadvice "0.3")) :commit "d533692182c084bad623977b69f9dc298255eaab" :authors '(("Philip Kaludercic" . "philipkNOSPAMposteo.net")) :maintainer '("Compat Development" . "~pkal/compat-develNOSPAMlists.sr.ht") :keywords '("lisp") :url "https://sr.ht/~pkal/compat")
--- /dev/null
+;;; compat.el --- Emacs Lisp Compatibility Library -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipkNOSPAMposteo.net>
+;; Maintainer: Compat Development <~pkal/compat-develNOSPAMlists.sr.ht>
+;; Version: 28.1.2.2
+;; URL: https://sr.ht/~pkal/compat
+;; Package-Requires: ((emacs "24.3") (nadvice "0.3"))
+;; Keywords: lisp
+
+;; 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:
+
+;; To allow for the usage of Emacs functions and macros that are
+;; defined in newer versions of Emacs, compat.el provides definitions
+;; that are installed ONLY if necessary. These reimplementations of
+;; functions and macros are at least subsets of the actual
+;; implementations. Be sure to read the documentation string to make
+;; sure.
+;;
+;; Not every function provided in newer versions of Emacs is provided
+;; here. Some depend on new features from the core, others cannot be
+;; implemented to a meaningful degree. Please consult the Compat
+;; manual for details. The main audience for this library are not
+;; regular users, but package maintainers. Therefore commands and
+;; user options are usually not implemented here.
+
+;;; Code:
+
+(defvar compat--inhibit-prefixed)
+(let ((compat--inhibit-prefixed (not (bound-and-true-p compat-testing))))
+ ;; Instead of using `require', we manually check `features' and call
+ ;; `load' to avoid the issue of not using `provide' at the end of
+ ;; the file (which is disabled by `compat--inhibit-prefixed', so
+ ;; that the file can be loaded again at some later point when the
+ ;; prefixed definitions are needed).
+ (dolist (vers '(24 25 26 27 28))
+ (unless (memq (intern (format "compat-%d" vers)) features)
+ (load (format "compat-%d%s" vers
+ (if (bound-and-true-p compat-testing)
+ ".el" ""))
+ nil t))))
+
+(provide 'compat)
+;;; compat.el ends here
--- /dev/null
+This is compat.info, produced by makeinfo version 6.7 from compat.texi.
+
+Copyright © 2022 Free Software Foundation, Inc.
+
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, with the Front-Cover Texts
+ being “A GNU Manual,” and with the Back-Cover Texts as in (a)
+ below. A copy of the license is included in the section entitled
+ “GNU Free Documentation License.”
+
+ (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and
+ modify this GNU manual.”
+
+INFO-DIR-SECTION Emacs
+START-INFO-DIR-ENTRY
+* Compat: (compat). Compatibility Library for Emacs Lisp.
+END-INFO-DIR-ENTRY
+
+\1f
+File: compat.info, Node: Top, Next: Introduction, Up: (dir)
+
+"Compat" Manual
+***************
+
+This manual documents the usage of the "Compat" Emacs lisp library, the
+forward-compatibility library for Emacs Lisp, corresponding to version
+28.1.2.2.
+
+ Copyright © 2022 Free Software Foundation, Inc.
+
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, with the Front-Cover Texts
+ being “A GNU Manual,” and with the Back-Cover Texts as in (a)
+ below. A copy of the license is included in the section entitled
+ “GNU Free Documentation License.”
+
+ (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and
+ modify this GNU manual.”
+
+* Menu:
+
+* Introduction::
+* Support::
+* Development::
+* Function Index::
+* Variable Index::
+
+— The Detailed Node Listing —
+
+Introduction
+
+* Overview::
+* Usage::
+* Intentions::
+
+Usage
+
+* Additional libraries::
+
+Support
+
+* Emacs 24.4:: Compatibility support for Emacs 24.4
+* Emacs 25.1:: Compatibility support for Emacs 25.1
+* Emacs 26.1:: Compatibility support for Emacs 26.1
+* Emacs 27.1:: Compatibility support for Emacs 27.1
+* Emacs 28.1:: Compatibility support for Emacs 28.1
+
+
+\1f
+File: compat.info, Node: Introduction, Next: Support, Prev: Top, Up: Top
+
+1 Introduction
+**************
+
+* Menu:
+
+* Overview::
+* Usage::
+* Intentions::
+
+\1f
+File: compat.info, Node: Overview, Next: Usage, Up: Introduction
+
+1.1 Overview
+============
+
+The objective of Compat is to provide "forwards compatibility" library
+for Emacs Lisp. That is to say by using Compat, an Elisp package does
+not have to make the decision to either use new and useful functionality
+or support old versions of Emacs.
+
+ Version 24.3 is chosen as the oldest version, because this is the
+newest version on CentOS 7. It is intended to preserve compatibility
+for at least as the Centos 7 reaches EOL
+(https://wiki.centos.org/About/Product), 2024.
+
+ If you are developing a package with Compat in mind, consider loading
+‘compat-help‘ (on your system, not in a package) to get relevant notes
+inserted into the help buffers of functions that are implemented or
+advised in Compat.
+
+ Note that Compat provides a few prefixed function, ie. functions
+with a ‘compat-’ prefix. These are used to provide extended
+functionality for commands that are already defined (‘sort’, ‘assoc’,
+‘seq’, ...). It might be possible to transform these into advised
+functions later on, so that the modified functionality is accessible
+without a prefix. Feedback on this point is appreciated.
+
+\1f
+File: compat.info, Node: Usage, Next: Intentions, Prev: Overview, Up: Introduction
+
+1.2 Usage
+=========
+
+The intended use-case for this library is for package developers to add
+as a dependency in the header:
+
+ ;; Package-Requires: ((emacs "24.3") (compat "28.1.2.2"))
+
+ There is no need to depend on ‘emacs’ 24.3 specifically. One can
+choose to any newer version, if features not provided by Compat
+necessitate it.
+
+ In any file where compatibility forms are used, a
+
+ (require 'compat)
+
+ should be added early on.
+
+ This will load all non-prefixed definitions (functions and macros
+with a leading ‘compat-‘). To load these, an additional
+
+ (require 'compat-XY) ; e.g. 26
+
+ will be necessary, to load compatibility code for Emacs version XY.
+
+ It is recommended to subscribe to the compat-announce
+(https://lists.sr.ht/~pkal/compat-announce) mailing list to be notified
+when new versions are released or relevant changes are made.
+
+* Menu:
+
+* Additional libraries::
+
+\1f
+File: compat.info, Node: Additional libraries, Up: Usage
+
+1.2.1 Additional libraries
+--------------------------
+
+These libraries are packages with Compat, but are disabled by default.
+To use them you can use ‘M-x load-library’:
+
+compat-help
+ Add notes to ‘*Help*’ buffer, if a compatibility definition has
+ something to warn you about.
+compat-font-lock
+ Highlight functions that are implemented as compatibility
+ definitions.
+
+\1f
+File: compat.info, Node: Intentions, Prev: Usage, Up: Introduction
+
+1.3 Intentions
+==============
+
+The library intends to provide support back until Emacs 24.3. The
+intended audience are package developers that are interested in using
+newer developments, without having to break compatibility.
+
+ Sadly, total backwards compatibility cannot be provided for technical
+reasons. These might include:
+
+ • An existing function or macro was extended by some new
+ functionality. To support these cases, the function or macro would
+ have to be advised. As this is usually regarded as invasive and is
+ shown to be a significant overhead, even when the new feature is
+ not used, this approach is not used.
+
+ As a compromise, prefixed functions and macros (starting with a
+ ‘compat-’ prefix) can be provided.
+
+ • New functionality was implemented in the core, and depends on
+ external libraries that cannot be reasonably duplicated in the
+ scope of a compatibility library.
+
+ • New functionality depends on an entire new, non-trivial library.
+ Sometimes these are provided via ELPA (xref, project, ...), but
+ other times it would be infeasible to duplicate an entire library
+ within Compat while also providing the necessary backwards
+ compatibility.
+
+ • It just wasn’t added, and there is no good reason (though good
+ excuses might exist). If you happen to find such a function, *note
+ reporting: Development. it would be much appreciated.
+
+ Always begin by assuming that this might be the case, unless proven
+ otherwise.
+
+\1f
+File: compat.info, Node: Support, Next: Development, Prev: Introduction, Up: Top
+
+2 Support
+*********
+
+This section goes into the features that Compat manages and doesn’t
+manage to provide for each Emacs version.
+
+* Menu:
+
+* Emacs 24.4:: Compatibility support for Emacs 24.4
+* Emacs 25.1:: Compatibility support for Emacs 25.1
+* Emacs 26.1:: Compatibility support for Emacs 26.1
+* Emacs 27.1:: Compatibility support for Emacs 27.1
+* Emacs 28.1:: Compatibility support for Emacs 28.1
+
+\1f
+File: compat.info, Node: Emacs 24.4, Next: Emacs 25.1, Up: Support
+
+2.1 Emacs 24.4
+==============
+
+2.1.1 Unprefixed Definitions
+----------------------------
+
+The following functions and macros implemented in 24.4, and are provided
+by Compat by default:
+
+ -- Macro: with-eval-after-load library body...
+ This macro arranges to evaluate BODY at the end of loading the file
+ LIBRARY, each time LIBRARY is loaded. If LIBRARY is already
+ loaded, it evaluates BODY right away.
+
+ You don’t need to give a directory or extension in the file name
+ LIBRARY. Normally, you just give a bare file name, like this:
+
+ (with-eval-after-load "js" (keymap-set js-mode-map "C-c C-c" 'js-eval))
+
+ To restrict which files can trigger the evaluation, include a
+ directory or an extension or both in LIBRARY. Only a file whose
+ absolute true name (i.e., the name with all symbolic links chased
+ out) matches all the given name components will match. In the
+ following example, ‘my_inst.elc’ or ‘my_inst.elc.gz’ in some
+ directory ‘..../foo/bar’ will trigger the evaluation, but not
+ ‘my_inst.el’:
+
+ (with-eval-after-load "foo/bar/my_inst.elc" ...)
+
+ LIBRARY can also be a feature (i.e., a symbol), in which case BODY
+ is evaluated at the end of any file where ‘(provide LIBRARY)’ is
+ called.
+
+ An error in BODY does not undo the load, but does prevent execution
+ of the rest of BODY.
+
+ *Note (elisp)Hooks for Loading::.
+
+ -- Function: special-form-p object
+ This predicate tests whether its argument is a special form, and
+ returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Special Forms::.
+
+ -- Function: macrop object
+ This predicate tests whether its argument is a macro, and returns
+ ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Simple Macro::.
+
+ -- Function: string-suffix-p suffix string &optional ignore-case
+ This function returns non-‘nil’ if SUFFIX is a suffix of STRING;
+ i.e., if STRING ends with SUFFIX. If the optional argument
+ IGNORE-CASE is non-‘nil’, the comparison ignores case differences.
+
+ *Note (elisp)Text Comparison::.
+
+ -- Function: delete-consecutive-dups list &optional circular
+ Destructively remove ‘equal’ consecutive duplicates from LIST.
+ First and last elements are considered consecutive if CIRCULAR is
+ non-nil.
+
+ -- Function: define-error name message &optional parent
+ In order for a symbol to be an error symbol, it must be defined
+ with ‘define-error’ which takes a parent condition (defaults to
+ ‘error’). This parent defines the conditions that this kind of
+ error belongs to. The transitive set of parents always includes
+ the error symbol itself, and the symbol ‘error’. Because quitting
+ is not considered an error, the set of parents of ‘quit’ is just
+ ‘(quit)’.
+
+ *Note (elisp)Error Symbols::.
+
+ -- Function: bool-vector-exclusive-or a b &optional c
+ Return “bitwise exclusive or” of bool vectors A and B. If optional
+ argument C is given, the result of this operation is stored into C.
+ All arguments should be bool vectors of the same length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-union a b &optional c
+ Return “bitwise or” of bool vectors A and B. If optional argument
+ C is given, the result of this operation is stored into C. All
+ arguments should be bool vectors of the same length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-intersection a b &optional c
+ Return “bitwise and” of bool vectors A and B. If optional argument
+ C is given, the result of this operation is stored into C. All
+ arguments should be bool vectors of the same length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-set-difference a b &optional c
+ Return “set difference” of bool vectors A and B. If optional
+ argument C is given, the result of this operation is stored into C.
+ All arguments should be bool vectors of the same length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-not a &optional b
+ Return “set complement” of bool vector A. If optional argument B
+ is given, the result of this operation is stored into B. All
+ arguments should be bool vectors of the same length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-subsetp a b
+ Return ‘t’ if every ‘t’ value in A is also ‘t’ in B, ‘nil’
+ otherwise. All arguments should be bool vectors of the same
+ length.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-count-consecutive a b i
+ Return the number of consecutive elements in A equal B starting at
+ I. ‘a’ is a bool vector, B is ‘t’ or ‘nil’, and I is an index into
+ ‘a’.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: bool-vector-count-population a
+ Return the number of elements that are ‘t’ in bool vector A.
+
+ *Note (elisp)Bool-Vectors::.
+
+ -- Function: completion-table-with-cache function &optional ignore-case
+ This is a wrapper for ‘completion-table-dynamic’ that saves the
+ last argument-result pair. This means that multiple lookups with
+ the same argument only need to call FUNCTION once. This can be
+ useful when a slow operation is involved, such as calling an
+ external process.
+
+ *Note (elisp)Programmed Completion::.
+
+ -- Function: face-spec-set face spec &optional spec-type
+ This function applies SPEC as a face spec for ‘face’. SPEC should
+ be a face spec, as described in the above documentation for
+ ‘defface’.
+
+ This function also defines FACE as a valid face name if it is not
+ already one, and (re)calculates its attributes on existing frames.
+
+ The optional argument SPEC-TYPE determines which spec to set. If
+ it is omitted or ‘nil’ or ‘face-override-spec’, this function sets
+ the “override spec”, which overrides face specs on FACE of all the
+ other types mentioned below. This is useful when calling this
+ function outside of Custom code. If SPEC-TYPE is ‘customized-face’
+ or ‘saved-face’, this function sets the customized spec or the
+ saved custom spec, respectively. If it is ‘face-defface-spec’,
+ this function sets the default face spec (the same one set by
+ ‘defface’). If it is ‘reset’, this function clears out all
+ customization specs and override specs from FACE (in this case, the
+ value of SPEC is ignored). The effect of any other value of
+ SPEC-TYPE on the face specs is reserved for internal use, but the
+ function will still define FACE itself and recalculate its
+ attributes, as described above.
+
+ *Note (elisp)Defining Faces::.
+
+2.1.2 Prefixed Definitions
+--------------------------
+
+These functions are prefixed with ‘compat’ prefix, and are only loaded
+when ‘compat-24’ is required:
+
+ -- Function: compat-= number-or-marker &rest number-or-markers
+ This function tests whether all its arguments are numerically
+ equal, and returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Comparison of Numbers::.
+ -- Function: compat-< number-or-marker &rest number-or-markers
+ This function tests whether each argument is strictly less than the
+ following argument. It returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Comparison of Numbers::.
+ -- Function: compat-> number-or-marker &rest number-or-markers
+ This function tests whether each argument is strictly greater than
+ the following argument. It returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Comparison of Numbers::.
+ -- Function: compat-<= number-or-marker &rest number-or-markers
+ This function tests whether each argument is less than or equal to
+ the following argument. It returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Comparison of Numbers::.
+ -- Function: compat->= number-or-marker &rest number-or-markers
+ This function tests whether each argument is greater than or equal
+ to the following argument. It returns ‘t’ if so, ‘nil’ otherwise.
+
+ *Note (elisp)Comparison of Numbers::.
+
+ These functions differ from the previous implementation in that they
+allow for more than two argument to be compared.
+
+ -- Function: compat-split-string string &optional separators omit-nulls
+ trim
+ This function splits STRING into substrings based on the regular
+ expression SEPARATORS (*note (elisp)Regular Expressions::). Each
+ match for SEPARATORS defines a splitting point; the substrings
+ between splitting points are made into a list, which is returned.
+
+ *note (elisp)Creating Strings:: for more details.
+
+ This version handles the optional argument TRIM. If it is
+ non-‘nil’, it should be a regular expression to match text to trim
+ from the beginning and end of each substring. If trimming makes
+ the substring empty, it is treated as null.
+
+2.1.3 Missing Definitions
+-------------------------
+
+Compat does not provide support for the following Lisp features
+implemented in 24.4:
+
+ • Allowing the second optional argument to ‘eval’ to specify a
+ lexical environment.
+ • The ‘define-alternatives’ macro.
+ • Support for the ‘defalias-fset-function’ symbol property.
+ • The ‘group-gid’ and ‘groupd-read-gid’ functions.
+ • The ‘pre-redisplay-function’ hook.
+ • Allowing for ‘with-demoted-errors’ to take a additional argument
+ ‘format’.
+ • The ‘add-face-text-property’ function.
+ • No ‘tty-setup-hook’ hook.
+ • The ‘get-pos-property’ function.
+ • The ‘define-advice’ macro.
+ • Support for generators.
+ • The ‘string-trim’, ‘string-trim-left’ and ‘string-trim-right’
+ functions. These are instead provided as prefixed function as part
+ of *note Emacs 26.1:: support.
+
+\1f
+File: compat.info, Node: Emacs 25.1, Next: Emacs 26.1, Prev: Emacs 24.4, Up: Support
+
+2.2 Emacs 25.1
+==============
+
+2.2.1 Unprefixed Definitions
+----------------------------
+
+The following functions and macros implemented in 25.1, and are provided
+by Compat by default:
+
+ -- Function: format-message string &rest objects
+ This function acts like ‘format’, except it also converts any grave
+ accents (`) and apostrophes (') in STRING as per the value of
+ ‘text-quoting-style’.
+
+ Typically grave accent and apostrophe in the format translate to
+ matching curved quotes, e.g., "Missing `%s'" might result in
+ "Missing ‘foo’". *Note (elisp)Text Quoting Style::, for how to
+ influence or inhibit this translation.
+
+ *note (elisp)Formatting Strings::.
+
+ -- Function: directory-name-p filename
+ This function returns non-‘nil’ if FILENAME ends with a directory
+ separator character. This is the forward slash ‘/’ on GNU and
+ other POSIX-like systems; MS-Windows and MS-DOS recognize both the
+ forward slash and the backslash ‘\’ as directory separators.
+
+ *Note (elisp)Directory Names::.
+
+ -- Function: string-greaterp string1 string2
+ This function returns the result of comparing STRING1 and STRING2
+ in the opposite order, i.e., it is equivalent to calling
+ ‘(string-lessp STRING2 STRING1)’.
+
+ *Note (elisp)Text Comparison::.
+
+ -- Macro: with-file-modes mode body...
+ This macro evaluates the BODY forms with the default permissions
+ for new files temporarily set to MODES (whose value is as for
+ ‘set-file-modes’ above). When finished, it restores the original
+ default file permissions, and returns the value of the last form in
+ BODY.
+
+ This is useful for creating private files, for example.
+
+ *Note (elisp)Changing Files::.
+
+ -- Function: alist-get key alist &optional default remove testfn
+ This function is similar to ‘assq’. It finds the first association
+ ‘(KEY . VALUE)’ by comparing KEY with ALIST elements, and, if
+ found, returns the VALUE of that association. If no association is
+ found, the function returns DEFAULT. Comparison of KEY against
+ ALIST elements uses the function specified by TESTFN, defaulting to
+ ‘eq’.
+
+ This is a generalized variable (*note (elisp)Generalized
+ Variables::) that can be used to change a value with ‘setf’. When
+ using it to set a value, optional argument REMOVE non-‘nil’ means
+ to remove KEY’s association from ALIST if the new value is ‘eql’ to
+ DEFAULT.
+
+ *note (elisp)Association Lists::.
+
+ -- Macro: if-let (bindings...) then &rest else...
+ As with ‘let*’, BINDINGS will consist of ‘(SYMBOL VALUE-FORM)’
+ entries that are evaluated and bound sequentially. If all
+ VALUE-FORM evaluate to non-‘nil’ values, then THEN is evaluated as
+ were the case with a regular ‘let*’ expression, with all the
+ variables bound. If any VALUE-FORM evaluates to ‘nil’, ELSE is
+ evaluated, without any bound variables.
+
+ A binding may also optionally drop the SYMBOL, and simplify to
+ ‘(VALUE-FORM)’ if only the test is of interest.
+
+ For the sake of backwards compatibility, it is possible to write a
+ single binding without a binding list:
+
+ (if-let* (SYMBOL (test)) foo bar)
+ ≡
+ (if-let* ((SYMBOL (test))) foo bar)
+
+ -- Macro: when-let (bindings...) &rest body
+ As with ‘when’, if one is only interested in the case where all
+ BINDINGS are non-nil. Otherwise BINDINGS are interpreted just as
+ they are by ‘if-let*’.
+
+ -- Macro: thread-first &rest forms
+ Combine FORMS into a single expression by “threading” each element
+ as the _first_ argument of their successor. Elements of FORMS can
+ either be an list of an atom.
+
+ For example, consider the threading expression and it’s equivalent
+ macro expansion:
+
+ (thread-first
+ 5
+ (+ 20)
+ (/ 25)
+ -
+ (+ 40))
+ ≡
+ (+ (- (/ (+ 5 20) 25)) 40)
+
+ Note how the single ‘-’ got converted into a list before threading.
+ This example uses arithmetic functions, but ‘thread-first’ is not
+ restricted to arithmetic or side-effect free code.
+
+ -- Macro: thread-last &rest forms
+ Combine FORMS into a single expression by “threading” each element
+ as the _last_ argument of their successor. Elements of FORMS can
+ either be an list of an atom.
+
+ For example, consider the threading expression and it’s equivalent
+ macro expansion:
+
+ (thread-first
+ 5
+ (+ 20)
+ (/ 25)
+ -
+ (+ 40))
+ ≡
+ (+ 40 (- (/ 25 (+ 20 5))))
+
+ Note how the single ‘-’ got converted into a list before threading.
+ This example uses arithmetic functions, but ‘thread-last’ is not
+ restricted to arithmetic or side-effect free code.
+
+ -- Function: macroexpand-1 form &optional environment
+ This function expands macros like ‘macroexpand’, but it only
+ performs one step of the expansion: if the result is another macro
+ call, ‘macroexpand-1’ will not expand it.
+
+ *Note Expansion: (elisp)Expansion.
+
+ -- Function: directory-files-recursively directory regexp &optional
+ include-directories predicate follow-symlinks
+ Return all files under DIRECTORY whose names match REGEXP. This
+ function searches the specified DIRECTORY and its sub-directories,
+ recursively, for files whose basenames (i.e., without the leading
+ directories) match the specified REGEXP, and returns a list of the
+ absolute file names of the matching files (*note absolute file
+ names: (elisp)Relative File Names.). The file names are returned
+ in depth-first order, meaning that files in some sub-directory are
+ returned before the files in its parent directory. In addition,
+ matching files found in each subdirectory are sorted alphabetically
+ by their basenames. By default, directories whose names match
+ REGEXP are omitted from the list, but if the optional argument
+ INCLUDE-DIRECTORIES is non-‘nil’, they are included.
+
+ By default, all subdirectories are descended into. If PREDICATE is
+ ‘t’, errors when trying to descend into a subdirectory (for
+ instance, if it’s not readable by this user) are ignored. If it’s
+ neither ‘nil’ nor ‘t’, it should be a function that takes one
+ parameter (the subdirectory name) and should return non-‘nil’ if
+ the directory is to be descended into.
+
+ Symbolic links to subdirectories are not followed by default, but
+ if FOLLOW-SYMLINKS is non-‘nil’, they are followed.
+
+ *Note (elisp)Contents of Directories::.
+
+ -- Function: bool-vector &rest objects
+ This function creates and returns a bool-vector whose elements are
+ the arguments, OBJECTS.
+
+ *Note (elisp)Bool-Vectors::.
+
+2.2.2 Prefixed Definitions
+--------------------------
+
+These functions are prefixed with ‘compat’ prefix, and are only loaded
+when ‘compat-25’ is required:
+
+ -- Function: compat-sort sequence predicate
+ This function sorts SEQUENCE stably. Note that this function
+ doesn’t work for all sequences; it may be used only for lists and
+ vectors. If SEQUENCE is a list, it is modified destructively.
+ This functions returns the sorted SEQUENCE and compares elements
+ using PREDICATE. A stable sort is one in which elements with equal
+ sort keys maintain their relative order before and after the sort.
+ Stability is important when successive sorts are used to order
+ elements according to different criteria.
+
+ *Note (elisp)Sequence Functions::.
+
+ The compatibility version adds support for vectors to be sorted,
+ not just lists.
+
+2.2.3 Missing Definitions
+-------------------------
+
+Compat does not provide support for the following Lisp features
+implemented in 25.1:
+
+ • New ‘pcase’ patterns.
+ • The hook ‘prefix-command-echo-keystrokes-functions’ and
+ ‘prefix-command-preserve-state-hook’.
+ • The hook ‘pre-redisplay-functions’.
+ • The function ‘make-process’.
+ • Support for the variable ‘inhibit-message’.
+ • The ‘define-inline’ functionality.
+ • The functions ‘string-collate-lessp’ and ‘string-collate-equalp’.
+ • The function ‘funcall-interactivly’.
+ • The function ‘buffer-substring-with-bidi-context’.
+ • The function ‘font-info’.
+ • The function ‘default-font-width’.
+ • The function ‘window-font-height’ and ‘window-font-width’.
+ • The function ‘window-max-chars-per-line’.
+ • The function ‘set-binary-mode’.
+ • The functions ‘bufferpos-to-filepos’ and ‘filepos-to-bufferpos’.
+
+\1f
+File: compat.info, Node: Emacs 26.1, Next: Emacs 27.1, Prev: Emacs 25.1, Up: Support
+
+2.3 Emacs 26.1
+==============
+
+2.3.1 Unprefixed Definitions
+----------------------------
+
+The following functions and macros implemented in 26.1, and are provided
+by Compat by default:
+
+ -- Function: func-arity function
+ This function provides information about the argument list of the
+ specified FUNCTION. The returned value is a cons cell of the form
+ ‘(MIN . MAX)’, where MIN is the minimum number of arguments, and
+ MAX is either the maximum number of arguments, or the symbol ‘many’
+ for functions with ‘&rest’ arguments, or the symbol ‘unevalled’ if
+ FUNCTION is a special form.
+
+ Note that this function might return inaccurate results in some
+ situations, such as the following:
+
+ − Functions defined using ‘apply-partially’ (*note
+ apply-partially: (elisp)Calling Functions.).
+
+ − Functions that are advised using ‘advice-add’ (*note
+ (elisp)Advising Named Functions::).
+
+ − Functions that determine the argument list dynamically, as
+ part of their code.
+
+ *Note (elisp)What Is a Function::.
+
+ -- Function: mapcan function sequence
+ This function applies FUNCTION to each element of SEQUENCE, like
+ ‘mapcar’, but instead of collecting the results into a list, it
+ returns a single list with all the elements of the results (which
+ must be lists), by altering the results (using ‘nconc’; *note
+ (elisp)Rearrangement::). Like with ‘mapcar’, SEQUENCE can be of
+ any type except a char-table.
+
+ ;; Contrast this: (mapcar #'list '(a b c d)) ⇒ ((a) (b) (c)
+ (d)) ;; with this: (mapcan #'list '(a b c d)) ⇒ (a b c d)
+
+ *Note (elisp)Mapping Functions::.
+
+ -- Function: cXXXr
+ -- Function: cXXXXr
+ *Note (elisp)List Elements::.
+
+ -- Function: gensym &optional prefix
+ This function returns a symbol using ‘make-symbol’, whose name is
+ made by appending ‘gensym-counter’ to PREFIX and incrementing that
+ counter, guaranteeing that no two calls to this function will
+ generate a symbol with the same name. The prefix defaults to
+ ‘"g"’.
+
+ -- Variable: gensym-counter
+ See ‘gensym’.
+
+ -- Function: make-nearby-temp-file prefix &optional dir-flag suffix
+ This function is similar to ‘make-temp-file’, but it creates a
+ temporary file as close as possible to ‘default-directory’. If
+ PREFIX is a relative file name, and ‘default-directory’ is a remote
+ file name or located on a mounted file systems, the temporary file
+ is created in the directory returned by the function
+ ‘temporary-file-directory’. Otherwise, the function
+ ‘make-temp-file’ is used. PREFIX, DIR-FLAG and SUFFIX have the
+ same meaning as in ‘make-temp-file’.
+
+ (let ((default-directory "/ssh:remotehost:")) (make-nearby-temp-file
+ "foo")) ⇒ "/ssh:remotehost:/tmp/foo232J6v"
+
+ -- Variable: mounted-file-systems
+ A regular expression matching files names that are probably on a
+ mounted file system.
+
+ -- Function: temporary-file-directory
+ The directory for writing temporary files via
+ ‘make-nearby-temp-file’. In case of a remote ‘default-directory’,
+ this is a directory for temporary files on that remote host. If
+ such a directory does not exist, or ‘default-directory’ ought to be
+ located on a mounted file system (see ‘mounted-file-systems’), the
+ function returns ‘default-directory’. For a non-remote and
+ non-mounted ‘default-directory’, the value of the variable
+ ‘temporary-file-directory’ is returned.
+
+ *Note (elisp)Unique File Names::.
+
+ -- Macro: if-let* (bindings...) then &rest else
+ ‘if-let*’ is mostly equivalent to ‘if-let’, with the exception that
+ the legacy ‘(if (VAR (test)) foo bar)’ syntax is not permitted.
+
+ -- Macro: when-let* (bindings...) then &rest else
+ ‘when-let*’ is mostly equivalent to ‘when-let’, with the exception
+ that the legacy ‘(when-let (VAR (test)) foo bar)’ syntax is not
+ permitted.
+
+ -- Macro: and-let* (bindings...) &rest body
+ A combination of LET* and AND, analogous to ‘when-let*’. If all
+ BINDINGS are non-‘nil’ and BODY is ‘nil’, then the result of the
+ ‘and-let*’ form will be the last value bound in BINDINGS.
+
+ **Please Note:** The implementation provided by Compat does not
+ include a bug that was observed with Emacs 26 (see
+ <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31840>).
+
+ -- Function: file-local-name filename
+ This function returns the _local part_ of FILENAME. This is the
+ part of the file’s name that identifies it on the remote host, and
+ is typically obtained by removing from the remote file name the
+ parts that specify the remote host and the method of accessing it.
+ For example:
+
+ (file-local-name "/ssh:USER@HOST:/foo/bar") ⇒
+ "/foo/bar"
+
+ For a remote FILENAME, this function returns a file name which
+ could be used directly as an argument of a remote process (*note
+ (elisp)Asynchronous Processes::, and *note (elisp)Synchronous
+ Processes::), and as the program to run on the remote host. If
+ FILENAME is local, this function returns it unchanged.
+
+ *Note (elisp)Magic File Names::.
+
+ -- Macro: file-name-quoted-p name
+ This macro returns non-‘nil’, when NAME is quoted with the prefix
+ ‘/:’. If NAME is a remote file name, the local part of NAME is
+ checked.
+
+ *Note (elisp)File Name Expansion::.
+
+ -- Macro: file-name-quote name
+ This macro adds the quotation prefix ‘/:’ to the file NAME. For a
+ local file NAME, it prefixes NAME with ‘/:’. If NAME is a remote
+ file name, the local part of NAME (*note (elisp)Magic File Names::)
+ is quoted. If NAME is already a quoted file name, NAME is returned
+ unchanged.
+
+ (substitute-in-file-name (file-name-quote "bar/~/foo")) ⇒
+ "/:bar/~/foo"
+
+ (substitute-in-file-name (file-name-quote "/ssh:host:bar/~/foo"))
+ ⇒ "/ssh:host:/:bar/~/foo"
+
+ The macro cannot be used to suppress file name handlers from magic
+ file names (*note (elisp)Magic File Names::).
+
+ *Note (elisp)File Name Expansion::.
+
+ -- Function: read-multiple-choice prompt choices &optional help-string
+ show-help long-form
+ 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.
+
+ If optional argument LONG-FORM is non-‘nil’, the user will have to
+ type in long-form answers (using ‘completing-read’) instead of
+ hitting a single key. The answers must be among the second
+ elements of the values in the CHOICES list.
+
+ Note: The Compat implementation of this function ignores the
+ optional arguments HELP-STRING and SHOW-HELP. Therefore the
+ optional third element in each CHOICES entry will also be
+ disregarded.
+
+ See *note Reading One Event: (elisp)Reading One Event.
+
+ -- Function: image-property
+ Defined in ‘image.el’.
+
+ This function can also be used as a generalised variable. To use
+ this you need to explicitly require ‘compat-26’.
+
+ -- Function: file-attribute-type
+ Return the field _type_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-link-number
+ Return the field _link-number_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-user-id
+ Return the field _user-id_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-group-id
+ Return the field _group-id_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-access-time
+ Return the field _access-time_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-modification-time
+ Return the field _modification-time_ as generated by
+ ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-status-change-time
+ Return the field _modification-time_ as generated by
+ ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-size
+ Return the field _size_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-modes
+ Return the field _modes_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-inode-number
+ Return the field _inode-number_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-device-number
+ Return the field _device-number_ as generated by ‘file-attributes’.
+
+ *Note (elisp)File Attributes::.
+
+ -- Function: file-attribute-collect attributes &rest attr-names
+ Filter the file attributes ATTRIBUTES, as generated by
+ ‘file-attributes’, according to ATTR-NAMES.
+
+ Valid attribute names for ATTR-NAMES are: type, link-number,
+ user-id, group-id, access-time, modification-time,
+ status-change-time, size, modes, inode-number and device-number.
+
+ (file-attributes ".") ⇒ (t 1 1000 1000 (25329 18215 325481 96000) (25325 15364 530263 840000) (25325 15364 530263 840000) 788 "drwxr-xr-x" t 137819 40)
+ (file-attribute-collect (file-attributes ".") 'type 'modes
+ 'inode-number) ⇒ (t "drwxr-xr-x" 137819)
+
+2.3.2 Prefixed Definitions
+--------------------------
+
+These functions are prefixed with ‘compat’ prefix, and are only loaded
+when ‘compat-26’ is required:
+
+ -- Function: compat-assoc key alist &optional testfn
+ This function returns the first association for KEY in ALIST,
+ comparing KEY against the alist elements using TESTFN if it is a
+ function, and ‘equal’ otherwise (*note (elisp)Equality
+ Predicates::). If TESTFN is a function, it is called with two
+ arguments: the CAR of an element from ALIST and KEY. The function
+ returns ‘nil’ if no association in ALIST has a CAR equal to KEY, as
+ tested by TESTFN.
+
+ *Note (elisp)Association Lists::.
+
+ The compatibility version adds support for handling the optional
+ argument TESTFN.
+
+ -- Function: compat-line-number-at-pos &optional pos absolute
+ This function returns the line number in the current buffer
+ corresponding to the buffer position POS. If POS is ‘nil’ or
+ omitted, the current buffer position is used. If ABSOLUTE is
+ ‘nil’, the default, counting starts at ‘(point-min)’, so the value
+ refers to the contents of the accessible portion of the
+ (potentially narrowed) buffer. If ABSOLUTE is non-‘nil’, ignore
+ any narrowing and return
+
+ *Note (elisp)Text Lines::.
+
+ The compatibility version adds support for handling the optional
+ argument ABSOLUTE.
+
+ -- Function: compat-alist-get key alist &optional default remove testfn
+ *Note (elisp)Association Lists::. This function is similar to
+ ‘assq’. It finds the first association ‘(KEY . VALUE)’ by
+ comparing KEY with ALIST elements, and, if found, returns the VALUE
+ of that association. If no association is found, the function
+ returns DEFAULT. Comparison of KEY against ALIST elements uses the
+ function specified by TESTFN, defaulting to ‘eq’.
+
+ *Note (elisp)Association Lists::.
+
+ The compatibility version handles the optional argument TESTFN. It
+ can also be used as a *note Generalized Variables:
+ (elisp)generalised variable.
+
+ -- Function: compat-string-trim-left string &optional regexp
+ Remove the leading text that matches REGEXP from STRING. REGEXP
+ defaults to ‘[ \t\n\r]+’.
+
+ *Note (elisp)Creating Strings::.
+
+ The compatibility version handles the optional argument REGEXP.
+
+ -- Function: compat-string-trim-right string &optional regexp
+ Remove the trailing text that matches REGEXP from STRING. REGEXP
+ defaults to ‘[ \t\n\r]+’.
+
+ *Note (elisp)Creating Strings::.
+
+ The compatibility version handles the optional argument REGEXP.
+
+ -- Function: compat-string-trim string &optional trim-left trim-right
+ Remove the leading text that matches TRIM-LEFT and trailing text
+ that matches TRIM-RIGHT from STRING. Both regexps default to ‘[
+ \t\n\r]+’.
+
+ *Note (elisp)Creating Strings::.
+
+ The compatibility version handles the optional arguments TRIM-LEFT
+ and TRIM-RIGHT.
+
+2.3.3 Missing Definitions
+-------------------------
+
+Compat does not provide support for the following Lisp features
+implemented in 26.1:
+
+ • The function ‘secure-hash-algorithms’.
+ • The function ‘gnutls-avalaible-p’.
+ • Support for records and record functions.
+ • The function ‘mapbacktrace’.
+ • The function ‘file-name-case-insensitive-p’.
+ • The file-attributes constructors.
+ • The additional elements of ‘parse-partial-sexp’.
+ • The function ‘add-variable-watcher’.
+ • The function ‘undo-amalgamate-change-group’.
+ • The function ‘char-from-name’
+ • Signalling errors when ‘length’ or ‘member’ deal with list cycles.
+ • The function ‘frame-list-z-order’.
+ • The function ‘frame-restack’.
+ • Support for side windows and atomic windows.
+ • All changes related to ‘display-buffer’.
+ • The function ‘window-swap-states’.
+
+\1f
+File: compat.info, Node: Emacs 27.1, Next: Emacs 28.1, Prev: Emacs 26.1, Up: Support
+
+2.4 Emacs 27.1
+==============
+
+2.4.1 Unprefixed Definitions
+----------------------------
+
+The following functions and macros implemented in 27.1, and are provided
+by Compat by default:
+
+ -- Function: proper-list-p object
+ This function returns the length of OBJECT if it is a proper list,
+ ‘nil’ otherwise (*note (elisp)Cons Cells::). In addition to
+ satisfying ‘listp’, a proper list is neither circular nor dotted.
+
+ (proper-list-p '(a b c)) ⇒ 3
+ (proper-list-p '(a b . c)) ⇒ nil
+
+ *Note (elisp)List-related Predicates::.
+
+ -- Function: string-distance string1 string2 &optional bytecompare
+ This function returns the _Levenshtein distance_ between the source
+ string STRING1 and the target string STRING2. The Levenshtein
+ distance is the number of single-character changes—deletions,
+ insertions, or replacements—required to transform the source string
+ into the target string; it is one possible definition of the _edit
+ distance_ between strings.
+
+ Letter-case of the strings is significant for the computed
+ distance, but their text properties are ignored. If the optional
+ argument BYTECOMPARE is non-‘nil’, the function calculates the
+ distance in terms of bytes instead of characters. The byte-wise
+ comparison uses the internal Emacs representation of characters, so
+ it will produce inaccurate results for multibyte strings that
+ include raw bytes (*note (elisp)Text Representations::); make the
+ strings unibyte by encoding them (*note (elisp)Explicit Encoding::)
+ if you need accurate results with raw bytes.
+
+ *Note (elisp)Text Comparison::.
+
+ -- Function: json-serialize object &rest args
+ This function returns a new Lisp string which contains the JSON
+ representation of OBJECT. The argument ARGS is a list of
+ keyword/argument pairs. The following keywords are accepted:
+
+ ‘:null-object’
+ The value decides which Lisp object to use to represent the
+ JSON keyword ‘null’. It defaults to the symbol ‘:null’.
+
+ ‘:false-object’
+ The value decides which Lisp object to use to represent the
+ JSON keyword ‘false’. It defaults to the symbol ‘:false’.
+
+ *Note (elisp)Parsing JSON::.
+
+ -- Function: json-insert object &rest args
+ This function inserts the JSON representation of OBJECT into the
+ current buffer before point. The argument ARGS are interpreted as
+ in ‘json-parse-string’.
+
+ *Note (elisp)Parsing JSON::.
+
+ -- Function: json-parse-string string &rest args
+ This function parses the JSON value in STRING, which must be a Lisp
+ string. If STRING doesn’t contain a valid JSON object, this
+ function signals the ‘json-parse-error’ error.
+
+ The argument ARGS is a list of keyword/argument pairs. The
+ following keywords are accepted:
+
+ ‘:object-type’
+ The value decides which Lisp object to use for representing
+ the key-value mappings of a JSON object. It can be either
+ ‘hash-table’, the default, to make hashtables with strings as
+ keys; ‘alist’ to use alists with symbols as keys; or ‘plist’
+ to use plists with keyword symbols as keys.
+
+ ‘:array-type’
+ The value decides which Lisp object to use for representing a
+ JSON array. It can be either ‘array’, the default, to use
+ Lisp arrays; or ‘list’ to use lists.
+
+ ‘:null-object’
+ The value decides which Lisp object to use to represent the
+ JSON keyword ‘null’. It defaults to the symbol ‘:null’.
+
+ ‘:false-object’
+ The value decides which Lisp object to use to represent the
+ JSON keyword ‘false’. It defaults to the symbol ‘:false’.
+
+ *Note (elisp)Parsing JSON::.
+
+ -- Function: json-parse-buffer &rest args
+ This function reads the next JSON value from the current buffer,
+ starting at point. It moves point to the position immediately
+ after the value if contains a valid JSON object; otherwise it
+ signals the ‘json-parse-error’ error and doesn’t move point. The
+ arguments ARGS are interpreted as in ‘json-parse-string’.
+
+ *Note (elisp)Parsing JSON::.
+
+ -- Macro: ignore-errors body...
+ This construct executes BODY, ignoring any errors that occur during
+ its execution. If the execution is without error, ‘ignore-errors’
+ returns the value of the last form in BODY; otherwise, it returns
+ ‘nil’.
+
+ Here’s the example at the beginning of this subsection rewritten
+ using ‘ignore-errors’:
+
+ (ignore-errors (delete-file filename))
+
+ *Note (elisp)Handling Errors::.
+
+ -- Macro: dolist-with-progress-reporter (var count [result])
+ reporter-or-message body...
+ This is another convenience macro that works the same way as
+ ‘dolist’ does, but also reports loop progress using the functions
+ described above. As in ‘dotimes-with-progress-reporter’,
+ ‘reporter-or-message’ can be a progress reporter or a string. You
+ can rewrite the previous example with this macro as follows:
+
+ (dolist-with-progress-reporter (k (number-sequence 0 500)) "Collecting
+ some mana for Emacs..." (sit-for 0.01))
+
+ *Note (elisp)Progress::.
+
+ -- Function: flatten-tree tree
+ This function returns a “flattened” copy of TREE, that is, a list
+ containing all the non-‘nil’ terminal nodes, or leaves, of the tree
+ of cons cells rooted at TREE. Leaves in the returned list are in
+ the same order as in TREE.
+
+ (flatten-tree '(1 (2 . 3) nil (4 5 (6)) 7)) ⇒(1 2 3 4 5 6 7)
+
+ *Note (elisp)Building Lists::.
+
+ -- Function: xor condition1 condition2
+ This function returns the boolean exclusive-or of CONDITION1 and
+ CONDITION2. That is, ‘xor’ returns ‘nil’ if either both arguments
+ are ‘nil’, or both are non-‘nil’. Otherwise, it returns the value
+ of that argument which is non-‘nil’.
+
+ Note that in contrast to ‘or’, both arguments are always evaluated.
+
+ *Note (elisp)Combining Conditions::.
+
+ -- Variable: regexp-unmatchable
+ This variable contains a regexp that is guaranteed not to match any
+ string at all. It is particularly useful as default value for
+ variables that may be set to a pattern that actually matches
+ something.
+
+ *Note (elisp)Regexp Functions::
+
+ -- Function: decoded-time-second time
+ Return the SECONDS field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-minute time
+ Return the MINUTE field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-hour time
+ Return the HOUR field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-day time
+ Return the DAY field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-month time
+ Return the MONTH field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-year time
+ Return the YEAR field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-weekday time
+ Return the WEEKDAY field of a ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-dst time
+ Return the DST (daylight saving time indicator) field of a
+ ‘decoded-time’ record TIME.
+
+ -- Function: decoded-time-zone time
+ Return the ZONE field of a ‘decoded-time’ record TIME.
+
+ -- Function: package-get-version
+ Return the version number of the package in which this is used.
+
+ -- Function: time-equal-p t1 t2
+ This returns ‘t’ if the two time values T1 and T2 are equal.
+
+ *Note (elisp)Time Calculations::.
+
+ -- Function: date-days-in-month year month
+ Return the number of days in MONTH in YEAR. For instance, February
+ 2020 has 29 days.
+
+ *Note (elisp)Time Calculations::. This function requires the
+ ‘time-date’ feature to be loaded.
+
+ -- User Option: exec-path
+ The value of this variable is a list of directories to search for
+ programs to run in subprocesses. Each element is either the name
+ of a directory (i.e., a string), or ‘nil’, which stands for the
+ default directory (which is the value of ‘default-directory’).
+ *Note executable-find: (elisp)Locating Files, for the details of
+ this search.
+
+ The value of ‘exec-path’ is used by ‘call-process’ and
+ ‘start-process’ when the PROGRAM argument is not an absolute file
+ name.
+
+ Generally, you should not modify ‘exec-path’ directly. Instead,
+ ensure that your ‘PATH’ environment variable is set appropriately
+ before starting Emacs. Trying to modify ‘exec-path’ independently
+ of ‘PATH’ can lead to confusing results.
+
+ *Note (elisp)Subprocess Creation::.
+
+ -- Function: provided-mode-derived-p mode &rest modes
+ This function returns non-‘nil’ if MODE is derived from any of the
+ major modes given by the symbols MODES.
+
+ -- Function: derived-mode-p &rest modes
+ This function returns non-‘nil’ if the current major mode is
+ derived from any of the major modes given by the symbols MODES.
+
+ *Note (elisp)Derived Modes::.
+
+2.4.2 Prefixed Definitions
+--------------------------
+
+These functions are prefixed with ‘compat’ prefix, and are only loaded
+when ‘compat-27’ is required:
+
+ -- Function: compat-recenter &optional count redisplay
+ This function scrolls the text in the selected window so that point
+ is displayed at a specified vertical position within the window.
+ It does not move point with respect to the text.
+
+ *Note (elisp)Textual Scrolling::.
+
+ This compatibility version adds support for the optional argument
+ REDISPLAY.
+
+ -- Function: compat-lookup-key keymap key &optional accept-defaults
+ This function returns the definition of KEY in KEYMAP. If the
+ string or vector KEY is not a valid key sequence according to the
+ prefix keys specified in KEYMAP, it must be too long and have extra
+ events at the end that do not fit into a single key sequence. Then
+ the value is a number, the number of events at the front of KEY
+ that compose a complete key.
+
+ *Note (elisp)Low-Level Key Binding::.
+
+ This compatibility version allows for KEYMAP to be a list of
+ keymaps, instead of just a singular keymap.
+
+ -- Macro: setq-local &rest pairs
+ PAIRS is a list of variable and value pairs. This macro creates a
+ buffer-local binding in the current buffer for each of the
+ variables, and gives them a buffer-local value. It is equivalent
+ to calling ‘make-local-variable’ followed by ‘setq’ for each of the
+ variables. The variables should be unquoted symbols.
+
+ (setq-local var1 "value1"
+ var2 "value2")
+
+ *Note (elisp)Creating Buffer-Local::.
+
+ This compatibility version allows for more than one variable to be
+ set at once, as can be done with ‘setq’.
+
+ -- Function: compat-regexp-opt strings &optional paren
+ This function returns an efficient regular expression that will
+ match any of the strings in the list STRINGS. This is useful when
+ you need to make matching or searching as fast as possible—for
+ example, for Font Lock mode.
+
+ *Note (elisp)Regexp Functions::.
+
+ The compatibility version of this functions handles the case where
+ STRINGS in an empty list. In that case, a regular expression is
+ generated that never matches anything (see ‘regexp-unmatchable’).
+
+ -- Function: compat-file-size-human-readable file-size &optional flavor
+ space unit
+ Return a string with a human readable representation of FILE-SIZE.
+
+ The optional second argument FLAVOR controls the units and the
+ display format. If FLAVOR is...
+
+ • ‘si’, each kilobyte is 1000 bytes and the produced suffixes
+ are ‘k’, ‘M’, ‘G’, ‘T’, etc.
+ • ‘iec’, each kilobyte is 1024 bytes and the produced suffixes
+ are ‘KiB’, ‘MiB’, ‘GiB’, ‘TiB’, etc.
+ • ‘nil’ or omitted, each kilobyte is 1024 bytes and the produced
+ suffixes are ‘k’, ‘M’, ‘G’, ‘T’, etc.
+
+ The compatibility version handles the optional third (SPACE) and
+ forth (UNIT) arguments. The argument SPACE can be a string that is
+ placed between the number and the unit. The argument UNIT
+ determines the unit to use. By default it will be an empty string,
+ unless FLAVOR is ‘iec’, in which case it will be ‘B’.
+
+ -- Function: compat-assoc-delete-all
+ This function is like ‘assq-delete-all’ except that it accepts an
+ optional argument TEST, a predicate function to compare the keys in
+ ALIST. If omitted or ‘nil’, TEST defaults to ‘equal’. As
+ ‘assq-delete-all’, this function often modifies the original list
+ structure of ALIST.
+
+ *Note (elisp)Association Lists::.
+
+ This compatibility version handles the optional third (TESTFN)
+ argument.
+
+ -- Function: compat-executable-find program &optional remote
+ This function searches for the executable file of the named PROGRAM
+ and returns the absolute file name of the executable, including its
+ file-name extensions, if any. It returns ‘nil’ if the file is not
+ found. The function searches in all the directories in
+ ‘exec-path’, and tries all the file-name extensions in
+ ‘exec-suffixes’ (*note (elisp)Subprocess Creation::).
+
+ If REMOTE is non-‘nil’, and ‘default-directory’ is a remote
+ directory, PROGRAM is searched on the respective remote host.
+
+ *Note (elisp)Locating Files::.
+
+ This compatibility version adds support to handle the optional
+ second (REMOTE) argument.
+
+ -- Function: compat-dired-get-marked-files &optional localp arg filter
+ distinguish-one-marked error
+ Return a list of file names that are _marked_ in a Dired buffer.
+
+ This compatibility version handles the optional fifth (ERROR)
+ argument, which signals an error if the list of found files is
+ empty. ‘error’ can be a string with the error message.
+
+2.4.3 Missing Definitions
+-------------------------
+
+Compat does not provide support for the following Lisp features
+implemented in 27.1:
+
+ • Bigint support.
+ • The function ‘time-convert’.
+ • All ‘iso8601-*’ functions.
+ • The macro ‘benchmark-progn’.
+ • The function ‘read-char-from-minibuffer’.
+ • The minor mode ‘reveal-mode’.
+ • The macro ‘with-suppressed-warnings’.
+ • Support for ‘condition-case’ to handle t.
+ • The functions ‘major-mode-suspend’ and ‘major-mode-restore’.
+ • The function ‘provided-mode-derived-p’.
+ • The function ‘file-system-info’.
+ • The more consistent treatment of NaN values.
+ • The function ‘ring-resize’.
+ • The function ‘group-name’.
+ • Additional ‘format-spec’ modifiers.
+ • Support for additional body forms for
+ ‘define-globalized-minor-mode’.
+ • The macro ‘with-connection-local-variables’ and related
+ functionality.
+
+\1f
+File: compat.info, Node: Emacs 28.1, Prev: Emacs 27.1, Up: Support
+
+2.5 Emacs 28.1
+==============
+
+2.5.1 Unprefixed Definitions
+----------------------------
+
+The following functions and macros implemented in 28.1, and are provided
+by Compat by default:
+
+ -- Function: string-search needle haystack &optional start-pos
+ Return the position of the first instance of NEEDLE in HAYSTACK,
+ both of which are strings. If START-POS is non-‘nil’, start
+ searching from that position in NEEDLE. Return ‘nil’ if no match
+ was found. This function only considers the characters in the
+ strings when doing the comparison; text properties are ignored.
+ Matching is always case-sensitive.
+
+ -- Function: length= sequence length
+ Return non-‘nil’ if the length of SEQUENCE is equal to LENGTH.
+
+ -- Function: length< sequence length
+ Return non-‘nil’ if SEQUENCE is shorter than LENGTH. This may be
+ more efficient than computing the length of SEQUENCE if SEQUENCE is
+ a long list.
+
+ -- Function: length> sequence length
+ Return non-‘nil’ if SEQUENCE is longer than LENGTH.
+
+ -- Function: file-name-concat directory &rest components
+ Concatenate COMPONENTS to DIRECTORY, inserting a slash before the
+ components if DIRECTORY or the preceding component didn’t end with
+ a slash.
+
+ (file-name-concat "/tmp" "foo") ⇒ "/tmp/foo"
+
+ A DIRECTORY or components that are ‘nil’ or the empty string are
+ ignored—they are filtered out first and do not affect the results
+ in any way.
+
+ This is almost the same as using ‘concat’, but DIRNAME (and the
+ non-final components) may or may not end with slash characters, and
+ this function will not double those characters.
+
+ -- Function: garbage-collect-maybe factor
+ Suggest to run garbage collection, if _enough_ data has been
+ allocated. This is determined by the positive numerical argument
+ FACTOR, that would proportionally increase the likelihood of
+ garbage collection taking place.
+
+ This compatibility function does nothing and ignores any
+ suggestion.
+
+ -- Function: string-replace from-string to-string in-string
+ This function replaces all occurrences of FROM-STRING with
+ TO-STRING in IN-STRING and returns the result. It may return one
+ of its arguments unchanged, a constant string or a new string.
+ Case is significant, and text properties are ignored.
+
+ -- Function: always &rest arguments
+ This function ignores any ARGUMENTS and returns ‘t’.
+
+ *Note (elisp)Calling Functions::.
+
+ -- Function: insert-into-buffer to-buffer &optional start end
+ This is like ‘insert-buffer-substring’, but works in the opposite
+ direction: The text is copied from the current buffer into
+ TO-BUFFER. The block of text is copied to the current point in
+ TO-BUFFER, and point (in that buffer) is advanced to after the end
+ of the copied text. Is ‘start’/‘end’ is ‘nil’, the entire text in
+ the current buffer is copied over.
+
+ *Note (elisp)Insertion::.
+
+ -- Function: replace-string-in-region regexp replacement &optional
+ start end
+ This function replaces all the occurrences of REGEXP with
+ REPLACEMENT in the region of buffer text between START and END;
+ START defaults to position of point, and END defaults to the last
+ accessible position of the buffer. The search for REGEXP is
+ case-sensitive, and REPLACEMENT is inserted without changing its
+ letter-case. The REPLACEMENT string can use the same special
+ elements starting with ‘\’ as ‘replace-match’ does. The function
+ returns the number of replaced occurrences, or ‘nil’ if REGEXP is
+ not found. The function preserves the position of point.
+
+ (replace-regexp-in-region "foo[ \t]+bar" "foobar")
+ *Note (elisp)Search and Replace::.
+
+ -- Function: replace-regexp-in-string string replacement &optional
+ start end
+ This function works similarly to ‘replace-regexp-in-region’, but
+ searches for, and replaces, literal STRINGs instead of regular
+ expressions.
+
+ *Note (elisp)Search and Replace::.
+
+ -- Function: buffer-local-boundp variable buffer
+ This returns non-‘nil’ if there’s either a buffer-local binding of
+ VARIABLE (a symbol) in buffer BUFFER, or VARIABLE has a global
+ binding.
+
+ *Note (elisp)Creating Buffer-Local::.
+
+ -- Macro: with-existing-directory body...
+ This macro ensures that ‘default-directory’ is bound to an existing
+ directory before executing BODY. If ‘default-directory’ already
+ exists, that’s preferred, and otherwise some other directory is
+ used. This macro can be useful, for instance, when calling an
+ external command that requires that it’s running in a directory
+ that exists. The chosen directory is not guaranteed to be
+ writable.
+
+ *Note (elisp)Testing Accessibility::.
+
+ -- Macro: dlet (bindings...) forms...
+ This special form is like ‘let’, but it binds all variables
+ dynamically. This is rarely useful—you usually want to bind normal
+ variables lexically, and special variables (i.e., variables that
+ are defined with ‘defvar’) dynamically, and this is what ‘let’
+ does.
+
+ ‘dlet’ can be useful when interfacing with old code that assumes
+ that certain variables are dynamically bound (*note (elisp)Dynamic
+ Binding::), but it’s impractical to ‘defvar’ these variables.
+ ‘dlet’ will temporarily make the bound variables special, execute
+ the forms, and then make the variables non-special again.
+
+ *Note (elisp)Local Variables::.
+
+ -- Function: ensure-list object
+ This function returns OBJECT as a list. If OBJECT is already a
+ list, the function returns it; otherwise, the function returns a
+ one-element list containing OBJECT.
+
+ This is usually useful if you have a variable that may or may not
+ be a list, and you can then say, for instance:
+
+ (dolist (elem (ensure-list foo))
+ (princ elem))
+
+ *Note (elisp)Building Lists::.
+
+ -- Function: string-clean-whitespace string
+ Clean up the whitespace in STRING by collapsing stretches of
+ whitespace to a single space character, as well as removing all
+ whitespace from the start and the end of STRING.
+
+ *Note (elisp)Creating Strings::.
+
+ -- Function: string-fill string length
+ Attempt to Word-wrap STRING so that no lines are longer than
+ LENGTH. Filling is done on whitespace boundaries only. If there
+ are individual words that are longer than LENGTH, these will not be
+ shortened.
+
+ *Note (elisp)Creating Strings::.
+
+ -- Function: string-lines string &optional omit-nulls keep-newlines
+ Split STRING into a list of strings on newline boundaries. If the
+ optional argument OMIT-NULLS is non-‘nil’, remove empty lines from
+ the results. If the optional argument KEEP-NEWLINES is non-‘nil’,
+ don’t remove the trailing newlines from the result strings.
+
+ *Note (elisp)Creating Strings::.
+
+ -- Function: string-pad string length &optional padding start
+ Pad STRING to be of the given LENGTH using PADDING as the padding
+ character. PADDING defaults to the space character. If STRING is
+ longer than LENGTH, no padding is done. If START is ‘nil’ or
+ omitted, the padding is appended to the characters of STRING, and
+ if it’s non-‘nil’, the padding is prepended to STRING’s characters.
+
+ *Note (elisp)Creating Strings::.
+
+ -- Function: string-chop-newline string
+ Remove the final newline, if any, from STRING.
+
+ *Note (elisp)Creating Strings::.
+
+ -- Macro: named-let name bindings &rest body
+ This special form is a looping construct inspired from the Scheme
+ language. It is similar to ‘let’: It binds the variables in
+ BINDINGS, and then evaluates BODY. However, ‘named-let’ also binds
+ NAME to a local function whose formal arguments are the variables
+ in BINDINGS and whose body is BODY. This allows BODY to call
+ itself recursively by calling NAME, where the arguments passed to
+ NAME are used as the new values of the bound variables in the
+ recursive invocation.
+
+ Recursive calls to NAME that occur in _tail positions_ in BODY are
+ guaranteed to be optimized as _tail calls_, which means that they
+ will not consume any additional stack space no matter how deeply
+ the recursion runs. Such recursive calls will effectively jump to
+ the top of the loop with new values for the variables.
+
+ *Note (elisp)Local Variables::.
+
+ -- Function: file-name-with-extension filename extension
+ This function returns FILENAME with its extension set to EXTENSION.
+ A single leading dot in the EXTENSION will be stripped if there is
+ one. For example:
+
+ (file-name-with-extension "file" "el")
+ ⇒ "file.el"
+ (file-name-with-extension "file" ".el")
+ ⇒ "file.el"
+ (file-name-with-extension "file.c" "el")
+ ⇒ "file.el"
+
+ Note that this function will error if FILENAME or EXTENSION are
+ empty, or if the FILENAME is shaped like a directory (i.e., if
+ ‘directory-name-p’ returns non-‘nil’).
+
+ *Note File Name Components: (elisp)File Name Components.
+
+ -- Function: directory-empty-p directory
+ This utility function returns ‘t’ if given DIRECTORY is an
+ accessible directory and it does not contain any files, i.e., is an
+ empty directory. It will ignore ‘.’ and ‘..’ on systems that
+ return them as files in a directory.
+
+ Symbolic links to directories count as directories. See
+ FILE-SYMLINK-P to distinguish symlinks.
+
+ *Note (elisp)Contents of Directories::.
+
+ -- Function: format-prompt prompt default &rest format-args
+ Format PROMPT with default value DEFAULT according to the
+ ‘minibuffer-default-prompt-format’ variable.
+
+ ‘minibuffer-default-prompt-format’ is a format string (defaulting
+ to ‘" (default %s)"’ that says how the “default” bit in prompts
+ like ‘"Local filename (default somefile): "’ are to be formatted.
+
+ To allow the users to customize how this is displayed, code that
+ prompts the user for a value (and has a default) should look
+ something along the lines of this code snippet:
+
+ (read-file-name
+ (format-prompt "Local filename" file)
+ nil file)
+
+ If FORMAT-ARGS is ‘nil’, PROMPT is used as a literal string. If
+ FORMAT-ARGS is non-‘nil’, PROMPT is used as a format control
+ string, and PROMPT and FORMAT-ARGS are passed to ‘format’ (*note
+ (elisp)Formatting Strings::).
+
+ ‘minibuffer-default-prompt-format’ can be ‘""’, in which case no
+ default values are displayed.
+
+ If DEFAULT is ‘nil’, there is no default value, and therefore no
+ “default value” string is included in the result value. If DEFAULT
+ is a non-‘nil’ list, the first element of the list is used in the
+ prompt.
+
+ *Note (elisp)Text from Minibuffer::.
+
+ -- Function: thing-at-mouse event thing &optional no-properties
+ Mouse-EVENT equivalent of ‘thing-at-point’. THING can be ‘symbol’,
+ ‘list’, ‘sexp’, ‘filename’, ‘url’, ... among other things.
+
+ When NO-PROPERTIES has a non-‘nil’ value, any text properties that
+ might have been present in the buffer are stripped away.
+
+ -- Function: macroexp-file-name
+ Return the name of the file in which the code is currently being
+ evaluated, or ‘nil’ if it cannot be determined.
+
+ -- Macro: with-environment-variables variables body...
+ This macro sets the environment variables according to VARIABLES
+ temporarily when executing BODY. The previous values are restored
+ when the form finishes. The argument VARIABLES should be a list of
+ pairs of strings of the form ‘(VAR VALUE)’, where VAR is the name
+ of the environment variable and VALUE is that variable’s value.
+
+ (with-environment-variables (("LANG" "C")
+ ("LANGUAGE" "en_US:en"))
+ (call-process "ls" nil t))
+
+ *Note System Environment: (elisp)System Environment.
+
+ -- Function: button-buttonize string callback &optional data help-echo
+ Return a button with STRING as its label. When interacted on, the
+ one-argument function in CALLBACK is called and DATA (or ‘nil’ is
+ not present) will be passed as the argument.
+
+ If non-‘nil’, the argument HELP-ECHO will be used to set the
+ ‘help-echo’ text property.
+
+ -- Function: make-directory-autoloads dir output-file
+ Parse and search the directory DIR for autoload definitions, and
+ write the processed results to the file OUTPUT-FILE.
+
+ -- Function: color-values-from-color-spec spec
+ Convert the textual color specification SPEC to a color triple
+ ‘(RED GREEN blue)’. Each of RED, GREEN and ‘blue’ is a integer
+ value between 0 and 65535.
+
+ The specification SPEC can be one of the following
+ • ‘#RGB’, where R, G and B are hex numbers of equal length, 1-4
+ digits each.
+ • ‘rgb:R/G/B’, where R, G, and B are hex numbers, 1-4 digits
+ each.
+ • ‘rgbi:R/G/B’, where R, G and B are floating-point numbers in
+ [0,1].
+
+ -- Function: file-modes-number-to-symbolic modes
+ This function converts a numeric file mode specification in MODES
+ into the equivalent symbolic form.
+
+ *Note Changing Files: (elisp)Changing Files.
+
+ -- Function: file-backup-file-names filename
+ This function returns a list of all the backup file names for
+ FILENAME, or ‘nil’ if there are none. The files are sorted by
+ modification time, descending, so that the most recent files are
+ first.
+
+ *Note (elisp)Backup Names::.
+
+ -- Function: make-lock-file-name filename
+ Return a string containing a lock file name for FILENAME, obeying
+ ‘lock-file-name-transforms’.
+
+ -- Function: null-device
+ Return the path to the null device (usually something like
+ ‘/dev/null’) on the current system.
+
+ -- Function: decoded-time-period time
+ Interpret TIME as a period and return its length in seconds. For
+ computational purposes, years are 365 days long and months are 30
+ days long.
+
+ -- Function: subr-primitive-p object
+ Return ‘t’ if OBJECT is a primitive, built-in function. On systems
+ with native compilation ‘subrp’ does not distinguish between
+ built-in functions and functions that have been compiled. If
+ native compilation is not avaliable, this function behaves
+ identically to ‘subrp’.
+
+ -- Function: file-name-absolute-p filename
+ This function returns ‘t’ if file FILENAME is an absolute file
+ name, ‘nil’ otherwise. A file name is considered to be absolute if
+ its first component is ‘~’, or is ‘~USER’ where USER is a valid
+ login name. In the following examples, assume that there is a user
+ named ‘rms’ but no user named ‘nosuchuser’.
+
+ (file-name-absolute-p "~rms/foo")
+ ⇒ t
+ (file-name-absolute-p "~nosuchuser/foo")
+ ⇒ nil
+ (file-name-absolute-p "rms/foo")
+ ⇒ nil
+ (file-name-absolute-p "/user/rms/foo")
+ ⇒ t
+
+ *Note (elisp)Absolute and Relative File Names::.
+
+2.5.2 Prefixed Definitions
+--------------------------
+
+These functions are prefixed with ‘compat’ prefix, and are only loaded
+when ‘compat-28’ is required:
+
+ -- Function: compat-unlock-buffer
+ This function unlocks the file being visited in the current buffer,
+ if the buffer is modified. If the buffer is not modified, then the
+ file should not be locked, so this function does nothing. It also
+ does nothing if the current buffer is not visiting a file, or is
+ not locked. This function handles file system errors by calling
+ ‘display-warning’ and otherwise ignores the error.
+
+ *Note (elisp)File Locks::.
+
+ This compatibility versions catches the ‘file-error’ condition,
+ issuing a warning instead of propagating on the error.
+
+ -- Function: compat-string-width string &optional from to
+ This function returns the width in columns of the string STRING, if
+ it were displayed in the current buffer and the selected window.
+ Optional arguments FROM and TO specify the substring of STRING to
+ consider, and are interpreted as in ‘substring’ (*note
+ (elisp)Creating Strings::).
+
+ The return value is an approximation: it only considers the values
+ returned by ‘char-width’ for the constituent characters, always
+ takes a tab character as taking ‘tab-width’ columns, ignores
+ display properties and fonts, etc.
+
+ *Note (elisp)Size of Displayed Text::.
+
+ This compatibility version handles the optional arguments FROM and
+ TO.
+
+ -- Function: compat-json-serialize
+ *Note Emacs 27.1::.
+
+ This compatibility version handles primitive, top-level JSON values
+ (numbers, strings, booleans).
+
+ -- Function: compat-json-insert
+ *Note Emacs 27.1::.
+
+ This compatibility version handles primitive, top-level JSON values
+ (numbers, strings, booleans).
+
+ -- Function: compat-json-parse-string
+ *Note Emacs 27.1::.
+
+ This compatibility version handles primitive, top-level JSON values
+ (numbers, strings, booleans).
+
+ -- Function: compat-json-parse-buffer
+ *Note Emacs 27.1::.
+
+ This compatibility version handles primitive, top-level JSON values
+ (numbers, strings, booleans).
+
+ -- Function: compat-count-windows
+ Return the number of live windows on the selected frame.
+
+ The optional argument MINIBUF specifies whether the minibuffer
+ window is included in the count.
+
+ If ALL-FRAMES is non-‘nil’, count the windows in all frames instead
+ just the selected frame.
+
+ This compatibility version handles the optional argument
+ ALL-FRAMES.
+
+2.5.3 Missing Definitions
+-------------------------
+
+Compat does not provide support for the following Lisp features
+implemented in 28.1:
+
+ • Support for ‘interactive’ or ‘declare’ to list applicable modes.
+ • Support for ‘:interactive’ argument to ‘define-minor-mode’ and
+ ‘define-derived-mode’.
+ • Support for ‘:predicate’ argument to
+ ‘define-globalized-minor-mode’.
+ • "Success handler" for ‘condition-case’.
+ • The function ‘benchmark-call’.
+ • Support for the ‘natnum’ defcustom type.
+ • The function ‘macroexp-compiling-p’.
+ • The function ‘macroexp-warn-and-return’.
+ • Additional Edebug keywords.
+ • Shorthand support.
+ • The function ‘custom-add-choice’.
+ • The function ‘decoded-time-period’.
+ • The function ‘dom-print’.
+ • The function ‘dom-remove-attribute’.
+ • The function ‘dns-query-asynchronous’.
+ • The function ‘get-locale-names’.
+ • The function ‘json-avaliable-p’.
+ • The function ‘mail-header-parse-addresses-lax’.
+ • The function ‘mail-header-parse-address-lax’.
+ • The function ‘make-separator-line’.
+ • The function ‘num-processors’.
+ • The function ‘object-intervals’.
+ • The function ‘process-lines-ignore-status’.
+ • The function ‘require-theme’.
+ • The function ‘syntax-class-to-char’.
+ • The function ‘path-separator’.
+
+\1f
+File: compat.info, Node: Development, Next: Function Index, Prev: Support, Up: Top
+
+3 Development
+*************
+
+Compat is developed on SourceHut (https://sr.ht/~pkal/compat). A
+restricted GitHub mirror (https://github.com/phikal/compat.el) is also
+maintained.
+
+ Patches and comments can be sent to the development mailing list
+(https://lists.sr.ht/~pkal/compat-devel) (~pkal/compat-develNOSPAMlists.sr.ht
+<~pkal/compat-develNOSPAMlists.sr.ht>). Bug reports are best sent to the
+issue tracker (https://todo.sr.ht/~pkal/compat) (~pkal/compatNOSPAMtodo.sr.ht
+<~pkal/compatNOSPAMtodo.sr.ht>). The GitHub mirror can also be used to
+submit patches. These may include issues in the compatibility code,
+missing definitions or performance issues.
+
+ Please note that as a GNU ELPA package, Compat requires contributors
+to have signed the FSF copyright assignment
+(https://www.gnu.org/software/emacs/manual/html_node/emacs/Copyright-Assignment.html),
+before any non-trivial contribution (roughly 15 lines of code) can be
+applied.
+
+\1f
+File: compat.info, Node: Function Index, Next: Variable Index, Prev: Development, Up: Top
+
+Appendix A Function Index
+*************************
+
+\0\b[index\0\b]
+* Menu:
+
+* alist-get: Emacs 25.1. (line 50)
+* always: Emacs 28.1. (line 61)
+* and-let*: Emacs 26.1. (line 99)
+* bool-vector: Emacs 25.1. (line 166)
+* bool-vector-count-consecutive: Emacs 24.4. (line 118)
+* bool-vector-count-population: Emacs 24.4. (line 125)
+* bool-vector-exclusive-or: Emacs 24.4. (line 76)
+* bool-vector-intersection: Emacs 24.4. (line 90)
+* bool-vector-not: Emacs 24.4. (line 104)
+* bool-vector-set-difference: Emacs 24.4. (line 97)
+* bool-vector-subsetp: Emacs 24.4. (line 111)
+* bool-vector-union: Emacs 24.4. (line 83)
+* buffer-local-boundp: Emacs 28.1. (line 99)
+* button-buttonize: Emacs 28.1. (line 285)
+* color-values-from-color-spec: Emacs 28.1. (line 297)
+* compat-<: Emacs 24.4. (line 175)
+* compat-<=: Emacs 24.4. (line 185)
+* compat-=: Emacs 24.4. (line 170)
+* compat->: Emacs 24.4. (line 180)
+* compat->=: Emacs 24.4. (line 190)
+* compat-alist-get: Emacs 26.1. (line 284)
+* compat-assoc: Emacs 26.1. (line 256)
+* compat-assoc-delete-all: Emacs 27.1. (line 305)
+* compat-count-windows: Emacs 28.1. (line 422)
+* compat-dired-get-marked-files: Emacs 27.1. (line 333)
+* compat-executable-find: Emacs 27.1. (line 317)
+* compat-file-size-human-readable: Emacs 27.1. (line 285)
+* compat-json-insert: Emacs 28.1. (line 404)
+* compat-json-parse-buffer: Emacs 28.1. (line 416)
+* compat-json-parse-string: Emacs 28.1. (line 410)
+* compat-json-serialize: Emacs 28.1. (line 398)
+* compat-line-number-at-pos: Emacs 26.1. (line 270)
+* compat-lookup-key: Emacs 27.1. (line 245)
+* compat-recenter: Emacs 27.1. (line 235)
+* compat-regexp-opt: Emacs 27.1. (line 273)
+* compat-sort: Emacs 25.1. (line 178)
+* compat-split-string: Emacs 24.4. (line 199)
+* compat-string-trim: Emacs 26.1. (line 314)
+* compat-string-trim-left: Emacs 26.1. (line 298)
+* compat-string-trim-right: Emacs 26.1. (line 306)
+* compat-string-width: Emacs 28.1. (line 381)
+* compat-unlock-buffer: Emacs 28.1. (line 368)
+* completion-table-with-cache: Emacs 24.4. (line 130)
+* cXXXr: Emacs 26.1. (line 47)
+* cXXXXr: Emacs 26.1. (line 48)
+* date-days-in-month: Emacs 27.1. (line 193)
+* decoded-time-day: Emacs 27.1. (line 166)
+* decoded-time-dst: Emacs 27.1. (line 178)
+* decoded-time-hour: Emacs 27.1. (line 163)
+* decoded-time-minute: Emacs 27.1. (line 160)
+* decoded-time-month: Emacs 27.1. (line 169)
+* decoded-time-period: Emacs 28.1. (line 332)
+* decoded-time-second: Emacs 27.1. (line 157)
+* decoded-time-weekday: Emacs 27.1. (line 175)
+* decoded-time-year: Emacs 27.1. (line 172)
+* decoded-time-zone: Emacs 27.1. (line 182)
+* define-error: Emacs 24.4. (line 65)
+* delete-consecutive-dups: Emacs 24.4. (line 60)
+* derived-mode-p: Emacs 27.1. (line 223)
+* directory-empty-p: Emacs 28.1. (line 218)
+* directory-files-recursively: Emacs 25.1. (line 139)
+* directory-name-p: Emacs 25.1. (line 24)
+* dlet: Emacs 28.1. (line 117)
+* dolist-with-progress-reporter: Emacs 27.1. (line 116)
+* ensure-list: Emacs 28.1. (line 132)
+* face-spec-set: Emacs 24.4. (line 139)
+* file-attribute-access-time: Emacs 26.1. (line 201)
+* file-attribute-collect: Emacs 26.1. (line 238)
+* file-attribute-device-number: Emacs 26.1. (line 233)
+* file-attribute-group-id: Emacs 26.1. (line 196)
+* file-attribute-inode-number: Emacs 26.1. (line 228)
+* file-attribute-link-number: Emacs 26.1. (line 186)
+* file-attribute-modes: Emacs 26.1. (line 223)
+* file-attribute-modification-time: Emacs 26.1. (line 206)
+* file-attribute-size: Emacs 26.1. (line 218)
+* file-attribute-status-change-time: Emacs 26.1. (line 212)
+* file-attribute-type: Emacs 26.1. (line 181)
+* file-attribute-user-id: Emacs 26.1. (line 191)
+* file-backup-file-names: Emacs 28.1. (line 316)
+* file-local-name: Emacs 26.1. (line 108)
+* file-modes-number-to-symbolic: Emacs 28.1. (line 310)
+* file-name-absolute-p: Emacs 28.1. (line 344)
+* file-name-concat: Emacs 28.1. (line 31)
+* file-name-quote: Emacs 26.1. (line 133)
+* file-name-quoted-p: Emacs 26.1. (line 126)
+* file-name-with-extension: Emacs 28.1. (line 200)
+* flatten-tree: Emacs 27.1. (line 129)
+* format-message: Emacs 25.1. (line 12)
+* format-prompt: Emacs 28.1. (line 229)
+* func-arity: Emacs 26.1. (line 12)
+* garbage-collect-maybe: Emacs 28.1. (line 46)
+* gensym: Emacs 26.1. (line 51)
+* if-let: Emacs 25.1. (line 67)
+* if-let*: Emacs 26.1. (line 90)
+* ignore-errors: Emacs 27.1. (line 103)
+* image-property: Emacs 26.1. (line 175)
+* insert-into-buffer: Emacs 28.1. (line 66)
+* json-insert: Emacs 27.1. (line 57)
+* json-parse-buffer: Emacs 27.1. (line 94)
+* json-parse-string: Emacs 27.1. (line 64)
+* json-serialize: Emacs 27.1. (line 42)
+* length<: Emacs 28.1. (line 23)
+* length=: Emacs 28.1. (line 20)
+* length>: Emacs 28.1. (line 28)
+* macroexp-file-name: Emacs 28.1. (line 267)
+* macroexpand-1: Emacs 25.1. (line 132)
+* macrop: Emacs 24.4. (line 47)
+* make-directory-autoloads: Emacs 28.1. (line 293)
+* make-lock-file-name: Emacs 28.1. (line 324)
+* make-nearby-temp-file: Emacs 26.1. (line 61)
+* mapcan: Emacs 26.1. (line 34)
+* named-let: Emacs 28.1. (line 182)
+* null-device: Emacs 28.1. (line 328)
+* package-get-version: Emacs 27.1. (line 185)
+* proper-list-p: Emacs 27.1. (line 12)
+* provided-mode-derived-p: Emacs 27.1. (line 219)
+* read-multiple-choice: Emacs 26.1. (line 151)
+* replace-regexp-in-string: Emacs 28.1. (line 91)
+* replace-string-in-region: Emacs 28.1. (line 76)
+* setq-local: Emacs 27.1. (line 258)
+* special-form-p: Emacs 24.4. (line 41)
+* string-chop-newline: Emacs 28.1. (line 177)
+* string-clean-whitespace: Emacs 28.1. (line 145)
+* string-distance: Emacs 27.1. (line 22)
+* string-fill: Emacs 28.1. (line 152)
+* string-greaterp: Emacs 25.1. (line 32)
+* string-lines: Emacs 28.1. (line 160)
+* string-pad: Emacs 28.1. (line 168)
+* string-replace: Emacs 28.1. (line 55)
+* string-search: Emacs 28.1. (line 12)
+* string-suffix-p: Emacs 24.4. (line 53)
+* subr-primitive-p: Emacs 28.1. (line 337)
+* temporary-file-directory: Emacs 26.1. (line 78)
+* thing-at-mouse: Emacs 28.1. (line 260)
+* thread-first: Emacs 25.1. (line 90)
+* thread-last: Emacs 25.1. (line 111)
+* time-equal-p: Emacs 27.1. (line 188)
+* when-let: Emacs 25.1. (line 85)
+* when-let*: Emacs 26.1. (line 94)
+* with-environment-variables: Emacs 28.1. (line 271)
+* with-eval-after-load: Emacs 24.4. (line 12)
+* with-existing-directory: Emacs 28.1. (line 106)
+* with-file-modes: Emacs 25.1. (line 39)
+* xor: Emacs 27.1. (line 139)
+
+\1f
+File: compat.info, Node: Variable Index, Prev: Function Index, Up: Top
+
+Appendix B Variable Index
+*************************
+
+\0\b[index\0\b]
+* Menu:
+
+* exec-path: Emacs 27.1. (line 200)
+* gensym-counter: Emacs 26.1. (line 58)
+* mounted-file-systems: Emacs 26.1. (line 74)
+* regexp-unmatchable: Emacs 27.1. (line 149)
+
+
+\1f
+Tag Table:
+Node: Top\7f821
+Node: Introduction\7f2274
+Node: Overview\7f2433
+Node: Usage\7f3656
+Node: Additional libraries\7f4663
+Node: Intentions\7f5118
+Node: Support\7f6728
+Node: Emacs 24.4\7f7310
+Node: Emacs 25.1\7f17364
+Node: Emacs 26.1\7f26321
+Node: Emacs 27.1\7f40419
+Node: Emacs 28.1\7f55775
+Node: Development\7f75469
+Node: Function Index\7f76484
+Node: Variable Index\7f87168
+\1f
+End Tag Table
+
+\1f
+Local Variables:
+coding: utf-8
+End:
--- /dev/null
+This is the file .../info/dir, which contains the
+topmost node of the Info hierarchy, called (dir)Top.
+The first time you invoke Info you start off looking at this node.
+\1f
+File: dir, Node: Top This is the top of the INFO tree
+
+ This (the Directory node) gives a menu of major topics.
+ Typing "q" exits, "H" lists all Info commands, "d" returns here,
+ "h" gives a primer for first-timers,
+ "mEmacs<Return>" visits the Emacs manual, etc.
+
+ In Emacs, you can click mouse button 2 on a menu item or cross reference
+ to select it.
+
+* Menu:
+
+Emacs
+* Compat: (compat). Compatibility Library for Emacs Lisp.