]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
add compat
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Sun, 11 Dec 2022 16:18:21 +0000 (17:18 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Sun, 11 Dec 2022 16:18:21 +0000 (17:18 +0100)
Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
17 files changed:
custom.el
elpa/compat-28.1.2.2.signed [new file with mode: 0644]
elpa/compat-28.1.2.2/.dir-locals.el [new file with mode: 0644]
elpa/compat-28.1.2.2/NEWS.org [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-24.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-25.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-26.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-27.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-28.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-autoloads.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-font-lock.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-help.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-macs.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat-pkg.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat.el [new file with mode: 0644]
elpa/compat-28.1.2.2/compat.info [new file with mode: 0644]
elpa/compat-28.1.2.2/dir [new file with mode: 0644]

index 5410c28231d64ab6d1ebef6e9d2d761ce76c6750..f7a8b3e7f3a10c8b8ee05f6a7268a537cc9c1f3d 100644 (file)
--- a/custom.el
+++ b/custom.el
@@ -6,17 +6,13 @@
  '(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
diff --git a/elpa/compat-28.1.2.2.signed b/elpa/compat-28.1.2.2.signed
new file mode 100644 (file)
index 0000000..0973a8f
--- /dev/null
@@ -0,0 +1 @@
+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
diff --git a/elpa/compat-28.1.2.2/.dir-locals.el b/elpa/compat-28.1.2.2/.dir-locals.el
new file mode 100644 (file)
index 0000000..900dee5
--- /dev/null
@@ -0,0 +1,7 @@
+;;; 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)))
diff --git a/elpa/compat-28.1.2.2/NEWS.org b/elpa/compat-28.1.2.2/NEWS.org
new file mode 100644 (file)
index 0000000..abb3448
--- /dev/null
@@ -0,0 +1,108 @@
+#+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>)
+
+
diff --git a/elpa/compat-28.1.2.2/compat-24.el b/elpa/compat-28.1.2.2/compat-24.el
new file mode 100644 (file)
index 0000000..d38f051
--- /dev/null
@@ -0,0 +1,495 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-25.el b/elpa/compat-28.1.2.2/compat-25.el
new file mode 100644 (file)
index 0000000..471fbee
--- /dev/null
@@ -0,0 +1,322 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-26.el b/elpa/compat-28.1.2.2/compat-26.el
new file mode 100644 (file)
index 0000000..98f7b2f
--- /dev/null
@@ -0,0 +1,675 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-27.el b/elpa/compat-28.1.2.2/compat-27.el
new file mode 100644 (file)
index 0000000..a96b1e9
--- /dev/null
@@ -0,0 +1,764 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-28.el b/elpa/compat-28.1.2.2/compat-28.el
new file mode 100644 (file)
index 0000000..a3dc769
--- /dev/null
@@ -0,0 +1,882 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-autoloads.el b/elpa/compat-28.1.2.2/compat-autoloads.el
new file mode 100644 (file)
index 0000000..8df8444
--- /dev/null
@@ -0,0 +1,42 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-font-lock.el b/elpa/compat-28.1.2.2/compat-font-lock.el
new file mode 100644 (file)
index 0000000..66a62e5
--- /dev/null
@@ -0,0 +1,48 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-help.el b/elpa/compat-28.1.2.2/compat-help.el
new file mode 100644 (file)
index 0000000..440e35f
--- /dev/null
@@ -0,0 +1,57 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-macs.el b/elpa/compat-28.1.2.2/compat-macs.el
new file mode 100644 (file)
index 0000000..c6417c8
--- /dev/null
@@ -0,0 +1,316 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat-pkg.el b/elpa/compat-28.1.2.2/compat-pkg.el
new file mode 100644 (file)
index 0000000..c3a4bf4
--- /dev/null
@@ -0,0 +1,2 @@
+;; 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")
diff --git a/elpa/compat-28.1.2.2/compat.el b/elpa/compat-28.1.2.2/compat.el
new file mode 100644 (file)
index 0000000..921f4f8
--- /dev/null
@@ -0,0 +1,58 @@
+;;; 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
diff --git a/elpa/compat-28.1.2.2/compat.info b/elpa/compat-28.1.2.2/compat.info
new file mode 100644 (file)
index 0000000..de21235
--- /dev/null
@@ -0,0 +1,2070 @@
+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:
diff --git a/elpa/compat-28.1.2.2/dir b/elpa/compat-28.1.2.2/dir
new file mode 100644 (file)
index 0000000..de02c6e
--- /dev/null
@@ -0,0 +1,18 @@
+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.