mirror of
https://github.com/doomemacs/doomemacs
synced 2025-08-01 12:17:25 -05:00
feat(lib): move backports to doom-compat.el
Moves backports to new doom-compat library. Also rolls out defbackport! calls, because it's unnecessary boilerplate.
This commit is contained in:
113
lisp/doom-compat.el
Normal file
113
lisp/doom-compat.el
Normal file
@ -0,0 +1,113 @@
|
||||
;;; lisp/doom-compat.el -*- lexical-binding: t; -*-
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This file backports functions and variables from future versions of Emacs (so
|
||||
;; Emacs 27.x users can enjoy them). Its goal is to support between Emacs 27.x
|
||||
;; and 30.x.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
;;; From Emacs 28+
|
||||
;; `format-spec' wasn't autoloaded until 28.1
|
||||
(unless (fboundp 'format-spec)
|
||||
(autoload 'format-spec "format-spec"))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(unless (fboundp 'ensure-list)
|
||||
(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."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(if (listp object) object (list object))))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(unless (fboundp 'always)
|
||||
(defun always (&rest _args)
|
||||
"Do nothing and return t.
|
||||
This function accepts any number of ARGUMENTS, but ignores them. Also see
|
||||
`ignore'."
|
||||
t))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(unless (fboundp 'file-name-concat)
|
||||
(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."
|
||||
(mapconcat
|
||||
#'identity
|
||||
(cl-loop for str in (cons directory components)
|
||||
if (and str (/= 0 (length str))
|
||||
(if (string-suffix-p "/" str)
|
||||
(substring str 0 -1)
|
||||
str))
|
||||
collect it)
|
||||
"/")))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(unless (fboundp 'with-environment-variables)
|
||||
(defmacro with-environment-variables (variables &rest body)
|
||||
"Set VARIABLES in the environment 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 restored upon exit."
|
||||
(declare (indent 1) (debug (sexp body)))
|
||||
(unless (consp variables)
|
||||
(error "Invalid VARIABLES: %s" variables))
|
||||
`(let ((process-environment (copy-sequence process-environment)))
|
||||
,@(cl-loop for var in variables
|
||||
collect `(setenv ,(car var) ,(cadr var)))
|
||||
,@body)))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(unless (fboundp 'file-name-with-extension)
|
||||
(defun file-name-with-extension (filename extension)
|
||||
"Return FILENAME modified to have the specified EXTENSION.
|
||||
The extension (in a file name) is the part that begins with the last \".\".
|
||||
This function removes any existing extension from FILENAME, and then
|
||||
appends EXTENSION to it.
|
||||
|
||||
EXTENSION may include the leading dot; if it doesn't, this function
|
||||
will provide it.
|
||||
|
||||
It is an error if FILENAME or EXTENSION is empty, or if FILENAME
|
||||
is in the form of a directory name according to `directory-name-p'.
|
||||
|
||||
See also `file-name-sans-extension'."
|
||||
(let ((extn (string-trim-left extension "[.]")))
|
||||
(cond ((string-empty-p filename)
|
||||
(error "Empty filename"))
|
||||
((string-empty-p extn)
|
||||
(error "Malformed extension: %s" extension))
|
||||
((directory-name-p filename)
|
||||
(error "Filename is a directory: %s" filename))
|
||||
((concat (file-name-sans-extension filename) "." extn))))))
|
||||
|
||||
|
||||
;;; From Emacs 29+
|
||||
;; Introduced in Emacs 29+
|
||||
(unless (fboundp 'with-memoization)
|
||||
(defmacro with-memoization (place &rest code)
|
||||
"Return the value of CODE and stash it in PLACE.
|
||||
If PLACE's value is non-nil, then don't bother evaluating CODE
|
||||
and return the value found in PLACE instead."
|
||||
(declare (indent 1) (debug (gv-place body)))
|
||||
(gv-letplace (getter setter) place
|
||||
`(or ,getter
|
||||
,(macroexp-let2 nil val (macroexp-progn code)
|
||||
`(progn
|
||||
,(funcall setter val)
|
||||
,val))))))
|
||||
|
||||
;; Introduced in emacs-mirror/emacs@f117b5df4dc6
|
||||
(unless (fboundp 'bol) (defalias 'bol #'line-beginning-position))
|
||||
(unless (fboundp 'eol) (defalias 'eol #'line-end-position))
|
||||
|
||||
(provide 'doom-compat)
|
||||
;;; doom-compat.el ends here
|
105
lisp/doom-lib.el
105
lisp/doom-lib.el
@ -2,6 +2,9 @@
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(when (< emacs-major-version 30)
|
||||
(require 'doom-compat))
|
||||
|
||||
;;; Custom error types
|
||||
(define-error 'doom-error "An unexpected Doom error")
|
||||
(dolist (type '((doom-font-error "Could not find a font on your system" doom-error)
|
||||
@ -1014,108 +1017,6 @@ testing advice (when combined with `rotate-text').
|
||||
(advice-remove target #',symbol)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Backports
|
||||
|
||||
(defmacro defbackport! (type symbol &rest body)
|
||||
"Backport a function/macro/alias from later versions of Emacs."
|
||||
(declare (indent defun) (doc-string 4))
|
||||
(unless (fboundp (doom-unquote symbol))
|
||||
`(,type ,symbol ,@body)))
|
||||
|
||||
;; `format-spec' wasn't autoloaded until 28.1
|
||||
(defbackport! autoload 'format-spec "format-spec")
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(defbackport! 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."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(if (listp object) object (list object)))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(defbackport! defun always (&rest _args)
|
||||
"Do nothing and return t.
|
||||
This function accepts any number of ARGUMENTS, but ignores them.
|
||||
Also see `ignore'."
|
||||
t)
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(defbackport! 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."
|
||||
(mapconcat
|
||||
#'identity
|
||||
(cl-loop for str in (cons directory components)
|
||||
if (and str (/= 0 (length str))
|
||||
(if (string-suffix-p "/" str)
|
||||
(substring str 0 -1)
|
||||
str))
|
||||
collect it)
|
||||
"/"))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(defbackport! defmacro with-environment-variables (variables &rest body)
|
||||
"Set VARIABLES in the environment 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 restored upon exit."
|
||||
(declare (indent 1) (debug (sexp body)))
|
||||
(unless (consp variables)
|
||||
(error "Invalid VARIABLES: %s" variables))
|
||||
`(let ((process-environment (copy-sequence process-environment)))
|
||||
,@(cl-loop for var in variables
|
||||
collect `(setenv ,(car var) ,(cadr var)))
|
||||
,@body))
|
||||
|
||||
;; Introduced in Emacs 28.1
|
||||
(defbackport! defun file-name-with-extension (filename extension)
|
||||
"Return FILENAME modified to have the specified EXTENSION.
|
||||
The extension (in a file name) is the part that begins with the last \".\".
|
||||
This function removes any existing extension from FILENAME, and then
|
||||
appends EXTENSION to it.
|
||||
|
||||
EXTENSION may include the leading dot; if it doesn't, this function
|
||||
will provide it.
|
||||
|
||||
It is an error if FILENAME or EXTENSION is empty, or if FILENAME
|
||||
is in the form of a directory name according to `directory-name-p'.
|
||||
|
||||
See also `file-name-sans-extension'."
|
||||
(let ((extn (string-trim-left extension "[.]")))
|
||||
(cond ((string-empty-p filename)
|
||||
(error "Empty filename"))
|
||||
((string-empty-p extn)
|
||||
(error "Malformed extension: %s" extension))
|
||||
((directory-name-p filename)
|
||||
(error "Filename is a directory: %s" filename))
|
||||
((concat (file-name-sans-extension filename) "." extn)))))
|
||||
|
||||
;; Introduced in Emacs 29+
|
||||
(defbackport! defmacro with-memoization (place &rest code)
|
||||
"Return the value of CODE and stash it in PLACE.
|
||||
If PLACE's value is non-nil, then don't bother evaluating CODE
|
||||
and return the value found in PLACE instead."
|
||||
(declare (indent 1) (debug (gv-place body)))
|
||||
(gv-letplace (getter setter) place
|
||||
`(or ,getter
|
||||
,(macroexp-let2 nil val (macroexp-progn code)
|
||||
`(progn
|
||||
,(funcall setter val)
|
||||
,val)))))
|
||||
|
||||
;; Introduced in Emacs 29+ (emacs-mirror/emacs@f117b5df4dc6)
|
||||
(defbackport! defalias 'bol #'line-beginning-position)
|
||||
(defbackport! defalias 'eol #'line-end-position)
|
||||
|
||||
|
||||
;;; Types
|
||||
|
||||
(cl-defstruct doom-module
|
||||
|
Reference in New Issue
Block a user