Archived
1
0
Fork 0

Compare commits

..

No commits in common. "e603e2b7ff613b9163d4187b0c8fb5b4437c0508" and "a988eae948efa1167c73f0773ffa47f27ed3a8cf" have entirely different histories.

786 changed files with 451861 additions and 10 deletions

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
*.elc
**ido.last**
**projectile-bookmarks**
**/auto-save-list
**/backups
**/bookmarks
**/persp-confs
code/projects
**/beancount-mode
**/transient
**/.org-id-locations
**/xapian-lite.so
**/projectile.cache
**/.persistent-scratch*
**/url
**/eshell
**/.lsp-session*
**/eln-cache
**/org-persist

View file

@ -1,15 +1,9 @@
## Important ## Important
- I no longer use `org-mode` as a habit / todo tracker I no longer use `org-mode` as a personal knowledge base / 2nd brain. I also no longer use it for any code editing.
- I no longer use `org-mode` as a personal knowledge base / 2nd brain
- I no longer use for code editing
- *I now use [Obsidian](https://obsidian.md) as a personal knowledge base / 2nd brain*
- *I now use [LogSeq](https://logseq.com/) with the `Org` file format as a habit and todo tracker combined with [Orgzly-Revived](https://github.com/orgzly-revived/orgzly-android-revived) on my Android mobile device(s)*
## The 'To Do and Habit` Config The main branch of the repo is now my config for *just* todo tracking, habit tracking, pomodoro timer and some basic time tracking.
If you are interested in a config thats focused on just todo and habit tracking, see the `todo-habit-confit` branch. ## The 'Real Config'
## The 'Big Config' If you are interested in a more complex config that shows how I leveraged emacs with multiple profiles and more, see the `pre-kb-removal` branch.
If you are interested in a more complex config that shows how I leveraged emacs with multiple profiles, todo / habit tracking, personal knowledge base and more, see the `big-config` branch.

View file

@ -0,0 +1,55 @@
;;; 0blayout-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from 0blayout.el
(autoload '0blayout-add-keybindings-with-prefix "0blayout" "\
Add 0blayout keybindings using the prefix PREFIX.
(fn PREFIX)")
(defvar 0blayout-mode nil "\
Non-nil if 0bLayout mode is enabled.
See the `0blayout-mode' command
for a description of this minor mode.")
(custom-autoload '0blayout-mode "0blayout" nil)
(autoload '0blayout-mode "0blayout" "\
Handle layouts with ease
This is a global minor mode. If called interactively, toggle the
`0bLayout mode' mode. If the prefix argument is positive, enable
the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `(default-value \\='0blayout-mode)'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(register-definition-prefixes "0blayout" '("0blayout-"))
;;; End of scraped data
(provide '0blayout-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; 0blayout-autoloads.el ends here

View file

@ -0,0 +1,6 @@
(define-package "0blayout" "20190703.527" "Layout grouping with ease" 'nil :commit "fd9a8f353dbd45b4628b5f84b8d8c2525ebf571d" :keywords
'("convenience" "window-management")
:url "https://github.com/etu/0blayout")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,200 @@
;;; 0blayout.el --- Layout grouping with ease
;; Copyright (c) 2015-2016 Elis Axelsson
;; Author: Elis "etu" Axelsson
;; URL: https://github.com/etu/0blayout
;; Package-Version: 20160918.0
;; Version: 1.0.2
;; Keywords: convenience, window-management
;;; Commentary:
;; This global minor mode provides a simple way to switch between layouts and
;; the buffers you left open before you switched (unless you closed it).
;; It doesn't require any setup at all more than:
;; (0blayout-mode)
;; When you start Emacs with 0blayout loaded, you will have a default layout
;; named "default", and then you can create new layouts (<prefix> C-c), switch
;; layouts (<prefix> C-b), and kill the current layout (<prefix> C-k).
;; The default <prefix> is (C-c C-l), but you can change it using:
;; (0blayout-add-keybindings-with-prefix "<your prefix>")
;; You can also customize-variable to change the name of the default session.
;; The project is hosted at https://github.com/etu/0blayout
;; There you can leave bug-reports and suggestions.
;; Another comparable mode is eyebrowse which have been developed for longer.
;;; License:
;; This file 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 file 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 file; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
;; 02110-1301, USA.
;;; Code:
(defgroup 0blayout nil
"Configuration settings for 0blayout-mode."
:group 'convenience)
(defvar 0blayout-alist ()
"List of the currently defined layouts.")
(defcustom 0blayout-default "default"
"Name of default layout used."
:type 'string
:group '0blayout)
(defvar 0blayout-keys-map '(("C-c" . 0blayout-new)
("C-k" . 0blayout-kill)
("C-b" . 0blayout-switch))
"Which keys bounded to which functions map.")
(defvar 0blayout-mode-map (make-sparse-keymap)
"Keymap for 0blayout.")
;; Function to create a new layout
(defun 0blayout-new (layout-name)
"0blayout creating function.
Argument LAYOUT-NAME Name of the layout."
(interactive "sEnter name of new layout: ")
;; Save the currently active layout
(0blayout-save)
;; Then we just delete all other windows and switch to a *scratch* buffer,
;; then it's up to the user to set up their layout.
(delete-other-windows)
(switch-to-buffer "*scratch*")
;; Save the name of the new current layout
(0blayout-set-current-name layout-name))
;; Function to kill current layout
(defun 0blayout-kill ()
"0blayout removal function."
(interactive)
(message "Killing layout: '%s'" (0blayout-get-current-name))
;; Remove current layout from known layouts
(setq 0blayout-alist
(assq-delete-all (intern (0blayout-get-current-name)) 0blayout-alist))
;; Switch to next layout in the list
(let ((new-layout (car (car 0blayout-alist))))
(if (eq new-layout nil)
;; If there's no other layout, make a new default layout
(progn
(0blayout-set-current-name 0blayout-default)
(0blayout-new 0blayout-default))
;; Switch to some other saved layout
(progn
(set-window-configuration (cdr (car 0blayout-alist)))
(0blayout-set-current-name (symbol-name new-layout))))))
;; Function to switch layout
(defun 0blayout-switch (layout-name)
"0blayout switching function.
Argument LAYOUT-NAME Name of the layout."
(interactive
(list
(completing-read "Layout to switch to: " 0blayout-alist)))
;; Save the currently active layout
(0blayout-save)
(let ((layout (assoc (intern layout-name) 0blayout-alist)))
(if (eq layout nil)
(message "No layout with name: '%s' is defined" layout-name)
(progn
;; Load window configuration
(set-window-configuration (cdr layout))
;; Save the name of the currently active layout
(0blayout-set-current-name layout-name)
(message "Switch to layout: '%s'" layout-name)))))
;; Function to save layout
(defun 0blayout-save ()
"This is a helper function to save the current layout."
;; Remove all saves of current layout before saving
(setq 0blayout-alist
(assq-delete-all
(intern (0blayout-get-current-name)) 0blayout-alist))
;; Add current layout to list
(add-to-list '0blayout-alist
(cons (intern (0blayout-get-current-name))
(current-window-configuration)))
(message "Saved the currently active layout: %s" (0blayout-get-current-name)))
;; Save current layout name
(defun 0blayout-set-current-name (layout-name)
"Helper function to store current LAYOUT-NAME for this frame."
(set-frame-parameter nil '0blayout-current layout-name))
;; Get current layout name
(defun 0blayout-get-current-name ()
"Helper function to get current LAYOUT-NAME for this frame."
;; Get variable from current frame
(let ((current-layout (frame-parameter nil '0blayout-current)))
;; Check if it's nil
(if (eq current-layout nil)
;; If so, return default value
0blayout-default
;; else return current value
current-layout)))
;;;###autoload
(defun 0blayout-add-keybindings-with-prefix (prefix)
"Add 0blayout keybindings using the prefix PREFIX."
(setf (cdr 0blayout-mode-map) nil)
(dolist (pair 0blayout-keys-map)
(define-key 0blayout-mode-map
(kbd (format "%s %s" prefix (car pair)))
(cdr pair))))
(0blayout-add-keybindings-with-prefix "C-c C-l")
;;;###autoload
(define-minor-mode 0blayout-mode
"Handle layouts with ease"
:lighter " 0bL"
:global t
:group '0blayout
:keymap 0blayout-mode-map)
(provide '0blayout)
;;; 0blayout.el ends here

View file

@ -0,0 +1,102 @@
;;; alert-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "alert" "alert.el" (0 0 0 0))
;;; Generated autoloads from alert.el
(autoload 'alert-add-rule "alert" "\
Programmatically add an alert configuration rule.
Normally, users should custoimze `alert-user-configuration'.
This facility is for module writers and users that need to do
things the Lisp way.
Here is a rule the author currently uses with ERC, so that the
fringe gets colored whenever people chat on BitlBee:
\(alert-add-rule :status \\='(buried visible idle)
:severity \\='(moderate high urgent)
:mode \\='erc-mode
:predicate
#\\='(lambda (info)
(string-match (concat \"\\\\`[^&].*@BitlBee\\\\\\='\")
(erc-format-target-and/or-network)))
:persistent
#\\='(lambda (info)
;; If the buffer is buried, or the user has been
;; idle for `alert-reveal-idle-time' seconds,
;; make this alert persistent. Normally, alerts
;; become persistent after
;; `alert-persist-idle-time' seconds.
(memq (plist-get info :status) \\='(buried idle)))
:style \\='fringe
:continue t)
\(fn &key SEVERITY STATUS MODE CATEGORY TITLE MESSAGE PREDICATE ICON (STYLE alert-default-style) PERSISTENT CONTINUE NEVER-PERSIST APPEND)" nil nil)
(autoload 'alert "alert" "\
Alert the user that something has happened.
MESSAGE is what the user will see. You may also use keyword
arguments to specify additional details. Here is a full example:
\(alert \"This is a message\"
:severity \\='high ;; The default severity is `normal'
:title \"Title\" ;; An optional title
:category \\='example ;; A symbol to identify the message
:mode \\='text-mode ;; Normally determined automatically
:buffer (current-buffer) ;; This is the default
:data nil ;; Unused by alert.el itself
:persistent nil ;; Force the alert to be persistent;
;; it is best not to use this
:never-persist nil ;; Force this alert to never persist
:id \\='my-id) ;; Used to replace previous message of
;; the same id in styles that support it
:style \\='fringe) ;; Force a given style to be used;
;; this is only for debugging!
:icon \\=\"mail-message-new\" ;; if style supports icon then add icon
;; name or path here
If no :title is given, the buffer-name of :buffer is used. If
:buffer is nil, it is the current buffer at the point of call.
:data is an opaque value which modules can pass through to their
own styles if they wish.
Here are some more typical examples of usage:
;; This is the most basic form usage
(alert \"This is an alert\")
;; You can adjust the severity for more important messages
(alert \"This is an alert\" :severity \\='high)
;; Or decrease it for purely informative ones
(alert \"This is an alert\" :severity \\='trivial)
;; Alerts can have optional titles. Otherwise, the title is the
;; buffer-name of the (current-buffer) where the alert originated.
(alert \"This is an alert\" :title \"My Alert\")
;; Further, alerts can have categories. This allows users to
;; selectively filter on them.
(alert \"This is an alert\" :title \"My Alert\"
:category \\='some-category-or-other)
\(fn MESSAGE &key (SEVERITY \\='normal) TITLE ICON CATEGORY BUFFER MODE DATA STYLE PERSISTENT NEVER-PERSIST ID)" nil nil)
(register-definition-prefixes "alert" '("alert-" "x-urgen"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; alert-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; Generated package description from alert.el -*- no-byte-compile: t -*-
(define-package "alert" "20221213.1619" "Growl-style notification system for Emacs" '((gntp "0.1") (log4e "0.3.0") (cl-lib "0.5")) :commit "c762380ff71c429faf47552a83605b2578656380" :authors '(("John Wiegley" . "jwiegley@gmail.com")) :maintainer '("John Wiegley" . "jwiegley@gmail.com") :keywords '("notification" "emacs" "message") :url "https://github.com/jwiegley/alert")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
;;; alert-toast-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "alert-toast" "alert-toast.el" (0 0 0 0))
;;; Generated autoloads from alert-toast.el
(autoload 'alert-toast-notify "alert-toast" "\
Send INFO using Windows 10 toast notification.
Handles :ICON, :SEVERITY, :PERSISTENT, :NEVER-PERSIST, :TITLE and
:MESSAGE keywords from INFO plist.
\(fn INFO)" nil nil)
(register-definition-prefixes "alert-toast" '("alert-toast-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; alert-toast-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; Generated package description from alert-toast.el -*- no-byte-compile: t -*-
(define-package "alert-toast" "20220312.229" "Windows 10 toast notifications" '((emacs "25.1") (alert "1.2") (f "0.20.0") (s "1.12.0")) :commit "96c88c93c1084de681700f655223142ee0eb944a" :authors '(("Grzegorz Kowzan" . "grzegorz@kowzan.eu")) :maintainer '("Grzegorz Kowzan" . "grzegorz@kowzan.eu") :url "https://github.com/gkowzan/alert-toast")

View file

@ -0,0 +1,316 @@
;;; alert-toast.el --- Windows 10 toast notifications -*- lexical-binding: t; -*-
;; Copyright 2020, 2022 Grzegorz Kowzan
;; Author: Grzegorz Kowzan <grzegorz@kowzan.eu>
;; Created: 25 Oct 2020
;; Updated: 25 Mar 2022
;; Version: 1.0.0
;; Package-Version: 20220312.229
;; Package-Commit: 96c88c93c1084de681700f655223142ee0eb944a
;; Package-Requires: ((emacs "25.1") (alert "1.2") (f "0.20.0") (s "1.12.0"))
;; Url: https://github.com/gkowzan/alert-toast
;; 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 2, 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This package defines a new alert style (`toast') for alert.el using Windows
;; 10 toast notifications. It works with native Windows 10 Emacs versions and
;; with Emacs run under Windows Subsystem for Linux (WSL) or under Cygwin. These
;; notifications are limited to a single-line title and four lines of text.
;; Longer text can be passed but it will be truncated by Windows 10.
;;
;; * Icons
;; Icons located on network shares are not supported. This includes icons on the
;; WSL virtual drive, therefore for Emacs running under WSL the default Emacs
;; icon is copied to C:\Users\<user>\AppData\Local\Emacs-Toast\Emacs.png. PNG
;; version is used because toast notifications render SVG graphics as tiny and
;; put them in top left corner of the notification.
;; Under WSL or Cygwin, a path to a custom icon should be given as a WSL/Cygwin
;; path (/mnt/c/... or /cygdrive/c/...) instead of a Windows path (C:\\...).
;;
;; * Priorities
;; Looking at Windows.UI.Notifications API, toast notifications seem to support
;; 2 priority levels: High and Default. Mapping between alert.el priorities and
;; these levels is defined by `alert-toast-priorities'.
;;
;; * Bugs
;; There is an issue in WSL where wslhost.exe dies for no discernible reason,
;; which prevents accessing Windows partitions and executables
;; (https://github.com/microsoft/WSL/issues/6161). If this happens then you
;; should see powershell.exe process failing to start. This will obviously
;; prevent this package from working. The only known workaround is to call `wsl
;; --shutdown' and start WSL again.
;;; Code:
(require 'f)
(require 's)
(require 'alert)
(require 'dom)
(require 'shr)
;; WSL-related functions and constants
;; In the words of WSL developers, there is no official way of testing for WSL
;; but they said at the same time that either "wsl" or "microsoft" should always
;; be present in the kernel release string.
(defun alert-toast--check-wsl ()
"Check if running under Windows Subsystem for Linux."
(and (eq system-type 'gnu/linux)
(let ((kernel-release (shell-command-to-string "uname --kernel-release")))
(or (s-contains? "wsl" kernel-release t)
(s-contains? "microsoft" kernel-release t)))))
(defconst alert-toast--wsl (alert-toast--check-wsl))
(defconst alert-toast--appdir-text "[System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData) | Join-Path -ChildPath Emacs-Toast\\Emacs.png")
(defun alert-toast--appdir ()
"Path to Windows user's data directory."
(with-temp-buffer
(let ((coding-system-for-read 'utf-8-dos))
(call-process-region alert-toast--appdir-text nil "powershell.exe" nil t nil "-noprofile" "-NonInteractive" "-WindowStyle" "Hidden" "-Command" "-"))
(s-chomp (buffer-string))))
(defun alert-toast--default-wsl-icon-path ()
"Path to Emacs icon in Windows user's data directory."
(with-temp-buffer
(call-process "wslpath" nil t nil (alert-toast--appdir))
(s-chomp (buffer-string))))
(defun alert-toast--init-wsl-icon ()
"Copy Emacs icon to a Windows-side directory."
(let ((icon-path (alert-toast--default-wsl-icon-path)))
(unless (f-exists? icon-path)
(make-directory (f-parent icon-path) t)
(f-copy (concat data-directory "images/icons/hicolor/128x128/apps/emacs.png")
icon-path))))
(defun alert-toast--icon-path (path)
"Convert icon PATH from WSL/Cygwin to Windows path if needed."
(cond
(alert-toast--wsl
(with-temp-buffer
(call-process "wslpath" nil t nil "-m" path)
(s-chomp (buffer-string))))
((eq system-type 'cygwin)
(with-temp-buffer
(call-process "cygpath.exe" nil t nil "-w" path)
(s-chomp (buffer-string))))
(t path)))
;; Default icon
(defvar alert-toast-default-icon
(if alert-toast--wsl
(alert-toast--default-wsl-icon-path)
(concat data-directory "images/icons/hicolor/128x128/apps/emacs.png"))
"Path to default icon for toast notifications.")
;; Common part -- script body, powershell quoting, priorities and main function
(defconst alert-toast--psquote-replacements
'(("'" . "''")))
(defcustom alert-toast-priorities
'((urgent . "[Windows.UI.Notifications.ToastNotificationPriority]::High")
(high . "[Windows.UI.Notifications.ToastNotificationPriority]::High")
(moderate . "[Windows.UI.Notifications.ToastNotificationPriority]::Default")
(normal . "[Windows.UI.Notifications.ToastNotificationPriority]::Default")
(low . "[Windows.UI.Notifications.ToastNotificationPriority]::Default")
(trivial . "[Windows.UI.Notifications.ToastNotificationPriority]::Default"))
"A mapping of alert severities onto Windows 10 toast priority values."
:type '(alist :key-type symbol :value-type string)
:group 'alert)
(defconst alert-toast--sounds
'((default . "ms-winsoundevent:Notification.Default")
(im . "ms-winsoundevent:Notification.IM")
(mail . "ms-winsoundevent:Notification.Mail")
(reminder . "ms-winsoundevent:Notification.Reminder")
(sms . "ms-winsoundevent:Notification.SMS"))
"Alist of available sounds.")
(defconst alert-toast--looping-sounds
(let ((looping-sounds '((call . "ms-winsoundevent:Notification.Looping.Call")
(alarm . "ms-winsoundevent:Notification.Looping.Alarm"))))
(dolist (i '(2 3 4 5 6 7 8 9 10) looping-sounds)
(setq looping-sounds
(cons `(,(intern (format "call%d" i)) . ,(format "ms-winsoundevent:Notification.Looping.Call%d" i))
looping-sounds))
(setq looping-sounds
(cons `(,(intern (format "alarm%d" i)) . ,(format "ms-winsoundevent:Notification.Looping.Alarm%d" i))
looping-sounds))))
"Alist of available looping sounds.")
(defvar alert-toast--psprocess nil
"Persistent powershell process emitting toast notifications.")
(defun alert-toast--coding-page ()
"Get powershell encoding."
(with-temp-buffer
(let ((coding-system-for-read 'utf-8-dos))
(call-process-region "[console]::InputEncoding.BodyName" nil "powershell.exe" nil t nil "-noprofile" "-NonInteractive" "-WindowStyle" "Hidden" "-Command" "-"))
(intern-soft (s-chomp (buffer-string)))))
(defun alert-toast--psprocess-init ()
"Initialize powershell process."
(setq alert-toast--psprocess
(make-process :name "powershell-toast"
:buffer "*powershell-toast*"
:command '("powershell.exe" "-noprofile" "-NoExit" "-NonInteractive" "-WindowStyle" "Hidden"
"-Command" "-")
:coding (if alert-toast--wsl 'utf-8 (alert-toast--coding-page))
:noquery t
:connection-type 'pipe))
(process-send-string alert-toast--psprocess "[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml, ContentType=WindowsRuntime] > $null\n"))
(defun alert-toast--psprocess-kill ()
"Kill powershell process (for debugging)."
(delete-process alert-toast--psprocess)
(setq alert-toast--psprocess nil))
(defun alert-toast--fill-template (title message icon-path &optional audio silent long loop)
"Create alert toast XML document.
Set title to TITLE, message body to MESSAGE and icon to the image at ICON-PATH.
ICON-PATH has to be a native Windows path, use `alert-toast--icon-path' for
Cygwin->native and WSL->native conversion.
AUDIO can be one of symbols defined in `alert-toast--sounds' or
`alert-toast--looping-sounds'. If SILENT is non-nil, the notification is muted.
If LONG is non-nil or one of the sounds in `alert-toast--looping-sounds' was
provided as AUDIO, then the notification will last for ~20 s; otherwise it lasts
for several seconds. Non-nil LOOP will loop the sound."
(let ((looping-sound (alist-get audio alert-toast--looping-sounds))
(dom
(dom-node
'toast nil
(dom-node
'visual nil
(dom-node
'binding
'((template . "ToastImageAndText02"))
(dom-node 'text '((id . "1")) title)
(dom-node 'text '((id . "2")) message)
(dom-node 'image `((id . "1")
(src . ,icon-path)
(placement . "appLogoOverride"))))))))
(when (or audio silent loop)
(dom-append-child
dom (dom-node 'audio `((src . ,(or looping-sound
(alist-get audio alert-toast--sounds)
(alist-get 'default alert-toast--sounds)))
(silent . ,(if silent "true" "false"))
(loop . ,(if (or loop looping-sound) "true" "false"))))))
(when (or long looping-sound)
(dom-set-attribute dom 'duration "long"))
(shr-dom-to-xml dom)))
(defun alert-toast--fill-shoulder (title message icon-path person payload)
"Create shoulder tap XML document.
PERSON is an email address given as 'mailto:login@domain.com' of a contact
previously added to My People. PAYLOAD is either remote http or local path to a
GIF or PNG image. Under WSL and Cygwin, local paths need to be converted to
native Windows paths with `alert-toast--icon-path'.
As a fallback, set title to TITLE, message body to MESSAGE and icon to the image
at ICON-PATH. ICON-PATH has to be a native Windows path, use
`alert-toast--icon-path' for Cygwin->native and WSL->native conversion."
(let ((dom
(dom-node 'toast
`((hint-people . ,person))
(dom-node 'visual nil
(dom-node 'binding '((template . "ToastGeneric"))
(dom-node 'text nil title)
(dom-node 'text nil message)
(dom-node 'image `((src . ,icon-path)
(placement . "appLogoOverride")
(hint-crop . "circle"))))
(dom-node 'binding '((template . "ToastGeneric")
(experienceType . "shoulderTap"))
(dom-node 'image `((src . ,payload))))))))
(shr-dom-to-xml dom)))
(defconst alert-toast--psscript-text "$Xml = New-Object Windows.Data.Xml.Dom.XmlDocument
$Xml.LoadXml('%s')
$Toast = [Windows.UI.Notifications.ToastNotification]::new($Xml)
$Toast.Tag = \"Emacs\"
$Toast.Group = \"Emacs\"
$Toast.Priority = %s
$Toast.ExpirationTime = [DateTimeOffset]::Now.AddSeconds(%f)
$Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(\"Emacs\")
$Notifier.Show($Toast);\n"
"Template of Powershell script emitting regular toast notification.")
(defconst alert-toast--psscript-shoulder "$Xml = New-Object Windows.Data.Xml.Dom.XmlDocument
$Xml.LoadXml('%s')
$Toast = [Windows.UI.Notifications.ToastNotification]::new($Xml)
$Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Microsoft.People_8wekyb3d8bbwe!x4c7a3b7dy2188y46d4ya362y19ac5a5805e5x')
$Notifier.Show($Toast);\n"
"Template of Powershell script emitting shoulder tap.")
;;;###autoload
(defun alert-toast-notify (info)
"Send INFO using Windows 10 toast notification.
Handles :ICON, :SEVERITY, :PERSISTENT, :NEVER-PERSIST, :TITLE and
:MESSAGE keywords from INFO plist."
(let ((data-plist (plist-get info :data))
psscript)
(if (and (plist-get data-plist :shoulder-person) (plist-get data-plist :shoulder-payload))
(setq psscript (format alert-toast--psscript-shoulder
(s-replace-all alert-toast--psquote-replacements
(alert-toast--fill-shoulder
(plist-get info :title)
(plist-get info :message)
(alert-toast--icon-path
(or (plist-get info :icon)
alert-toast-default-icon))
(plist-get data-plist :shoulder-person)
(plist-get data-plist :shoulder-payload)))))
(setq psscript
(format alert-toast--psscript-text
(s-replace-all alert-toast--psquote-replacements
(alert-toast--fill-template
(plist-get info :title)
(plist-get info :message)
(alert-toast--icon-path (or (plist-get info :icon) alert-toast-default-icon))
(plist-get data-plist :audio)
(plist-get data-plist :silent)
(plist-get data-plist :long)
(plist-get data-plist :loop)))
(or (cdr (assq (plist-get info :severity) alert-toast-priorities))
(cdr (assq 'normal alert-toast-priorities)))
(if (and (plist-get info :persistent)
(not (plist-get info :never-persist)))
(* 60 60 24 7) ; a week
alert-fade-time))))
(unless alert-toast--psprocess
(alert-toast--psprocess-init))
(process-send-string alert-toast--psprocess psscript)))
(alert-define-style 'toast :title "Windows 10 toast notification"
:notifier #'alert-toast-notify)
(when alert-toast--wsl
(alert-toast--init-wsl-icon))
(provide 'alert-toast)
;;; alert-toast.el ends here

View file

@ -0,0 +1,70 @@
;;; all-the-icons-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from all-the-icons.el
(autoload 'all-the-icons-icon-for-dir "all-the-icons" "\
Get the formatted icon for DIR.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions.
Note: You want chevron, please use `all-the-icons-icon-for-dir-with-chevron'.
(fn DIR &rest ARG-OVERRIDES)")
(autoload 'all-the-icons-icon-for-file "all-the-icons" "\
Get the formatted icon for FILE.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions.
(fn FILE &rest ARG-OVERRIDES)")
(autoload 'all-the-icons-icon-for-mode "all-the-icons" "\
Get the formatted icon for MODE.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions.
(fn MODE &rest ARG-OVERRIDES)")
(autoload 'all-the-icons-icon-for-url "all-the-icons" "\
Get the formatted icon for URL.
If an icon for URL isn't found in `all-the-icons-url-alist', a globe is used.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions.
(fn URL &rest ARG-OVERRIDES)")
(autoload 'all-the-icons-install-fonts "all-the-icons" "\
Helper function to download and install the latests fonts based on OS.
When PFX is non-nil, ignore the prompt and just install
(fn &optional PFX)" t)
(autoload 'all-the-icons-insert "all-the-icons" "\
Interactive icon insertion function.
When Prefix ARG is non-nil, insert the propertized icon.
When FAMILY is non-nil, limit the candidates to the icon set matching it.
(fn &optional ARG FAMILY)" t)
(register-definition-prefixes "all-the-icons" '("all-the-icons-"))
;;; End of scraped data
(provide 'all-the-icons-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; all-the-icons-autoloads.el ends here

View file

@ -0,0 +1,230 @@
;;; all-the-icons-faces.el --- A module of faces for all-the-icons
;; Copyright (C) 2016 Dominic Charlesworth <dgc336@gmail.com>
;; Author: Dominic Charlesworth <dgc336@gmail.com>
;; Version: 1.0.0
;; Package-Requires: ((emacs "24.3"))
;; URL: https://github.com/domtronn/all-the-icons.el
;; Keywords: convenient, 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file contains all of the faces used by the package for
;; colouring icons
;;; Code:
(defgroup all-the-icons-faces nil
"Manage how All The Icons icons are coloured and themed."
:prefix "all-the-icons-"
:group 'tools
:group 'all-the-icons)
;; red
(defface all-the-icons-red
'((((background dark)) :foreground "#AC4142")
(((background light)) :foreground "#AC4142"))
"Face for red icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lred
'((((background dark)) :foreground "#EB595A")
(((background light)) :foreground "#EB595A"))
"Face for lred icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dred
'((((background dark)) :foreground "#843031")
(((background light)) :foreground "#843031"))
"Face for dred icons"
:group 'all-the-icons-faces)
(defface all-the-icons-red-alt
'((((background dark)) :foreground "#ce5643")
(((background light)) :foreground "#843031"))
"Face for dred icons"
:group 'all-the-icons-faces)
;; green
(defface all-the-icons-green
'((((background dark)) :foreground "#90A959")
(((background light)) :foreground "#90A959"))
"Face for green icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lgreen
'((((background dark)) :foreground "#C6E87A")
(((background light)) :foreground "#3D6837"))
"Face for lgreen icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dgreen
'((((background dark)) :foreground "#6D8143")
(((background light)) :foreground "#6D8143"))
"Face for dgreen icons"
:group 'all-the-icons-faces)
;; yellow
(defface all-the-icons-yellow
'((((background dark)) :foreground "#FFD446")
(((background light)) :foreground "#FFCC0E"))
"Face for yellow icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lyellow
'((((background dark)) :foreground "#FFC16D")
(((background light)) :foreground "#FF9300"))
"Face for lyellow icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dyellow
'((((background dark)) :foreground "#B48D56")
(((background light)) :foreground "#B48D56"))
"Face for dyellow icons"
:group 'all-the-icons-faces)
;; blue
(defface all-the-icons-blue
'((((background dark)) :foreground "#6A9FB5")
(((background light)) :foreground "#6A9FB5"))
"Face for blue icons"
:group 'all-the-icons-faces)
(defface all-the-icons-blue-alt
'((((background dark)) :foreground "#2188b6")
(((background light)) :foreground "#2188b6"))
"Face for blue icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lblue
'((((background dark)) :foreground "#8FD7F4")
(((background light)) :foreground "#677174"))
"Face for lblue icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dblue
'((((background dark)) :foreground "#446674")
(((background light)) :foreground "#446674"))
"Face for dblue icons"
:group 'all-the-icons-faces)
;; maroon
(defface all-the-icons-maroon
'((((background dark)) :foreground "#8F5536")
(((background light)) :foreground "#8F5536"))
"Face for maroon icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lmaroon
'((((background dark)) :foreground "#CE7A4E")
(((background light)) :foreground "#CE7A4E"))
"Face for lmaroon icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dmaroon
'((((background dark)) :foreground "#72584B")
(((background light)) :foreground "#72584B"))
"Face for dmaroon icons"
:group 'all-the-icons-faces)
;; purple
(defface all-the-icons-purple
'((((background dark)) :foreground "#AA759F")
(((background light)) :foreground "#68295B"))
"Face for purple icons"
:group 'all-the-icons-faces)
(defface all-the-icons-purple-alt
'((((background dark)) :foreground "#5D54E1")
(((background light)) :foreground "#5D54E1"))
"Face for purple icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lpurple
'((((background dark)) :foreground "#E69DD6")
(((background light)) :foreground "#E69DD6"))
"Face for lpurple icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dpurple
'((((background dark)) :foreground "#694863")
(((background light)) :foreground "#694863"))
"Face for dpurple icons"
:group 'all-the-icons-faces)
;; orange
(defface all-the-icons-orange
'((((background dark)) :foreground "#D4843E")
(((background light)) :foreground "#D4843E"))
"Face for orange icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lorange
'((((background dark)) :foreground "#FFA500")
(((background light)) :foreground "#FFA500"))
"Face for lorange icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dorange
'((((background dark)) :foreground "#915B2D")
(((background light)) :foreground "#915B2D"))
"Face for dorange icons"
:group 'all-the-icons-faces)
;; cyan
(defface all-the-icons-cyan
'((((background dark)) :foreground "#75B5AA")
(((background light)) :foreground "#75B5AA"))
"Face for cyan icons"
:group 'all-the-icons-faces)
(defface all-the-icons-cyan-alt
'((((background dark)) :foreground "#61dafb")
(((background light)) :foreground "#0595bd"))
"Face for cyan icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lcyan
'((((background dark)) :foreground "#A5FDEC")
(((background light)) :foreground "#2C7D6E"))
"Face for lcyan icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dcyan
'((((background dark)) :foreground "#48746D")
(((background light)) :foreground "#48746D"))
"Face for dcyan icons"
:group 'all-the-icons-faces)
;; pink
(defface all-the-icons-pink
'((((background dark)) :foreground "#F2B4B8")
(((background light)) :foreground "#FC505B"))
"Face for pink icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lpink
'((((background dark)) :foreground "#FFBDC1")
(((background light)) :foreground "#FF505B"))
"Face for lpink icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dpink
'((((background dark)) :foreground "#B18286")
(((background light)) :foreground "#7E5D5F"))
"Face for dpink icons"
:group 'all-the-icons-faces)
;; silver
(defface all-the-icons-silver
'((((background dark)) :foreground "#716E68")
(((background light)) :foreground "#716E68"))
"Face for silver icons"
:group 'all-the-icons-faces)
(defface all-the-icons-lsilver
'((((background dark)) :foreground "#B9B6AA")
(((background light)) :foreground "#7F7869"))
"Face for lsilver icons"
:group 'all-the-icons-faces)
(defface all-the-icons-dsilver
'((((background dark)) :foreground "#838484")
(((background light)) :foreground "#838484"))
"Face for dsilver icons"
:group 'all-the-icons-faces)
(provide 'all-the-icons-faces)
;;; all-the-icons-faces.el ends here

View file

@ -0,0 +1,14 @@
(define-package "all-the-icons" "20240623.1800" "A library for inserting Developer icons"
'((emacs "24.3"))
:commit "39ef44f810c34e8900978788467cc675870bcd19" :authors
'(("Dominic Charlesworth" . "dgc336@gmail.com"))
:maintainers
'(("Dominic Charlesworth" . "dgc336@gmail.com"))
:maintainer
'("Dominic Charlesworth" . "dgc336@gmail.com")
:keywords
'("convenient" "lisp")
:url "https://github.com/domtronn/all-the-icons.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
(defvar all-the-icons-data/alltheicons-alist
'(
( "apache" . "\xe909" )
( "atom" . "\xe917" )
( "aws" . "\xe90c" )
( "bower" . "\xe918" )
( "c" . "\xe915" )
( "c-line" . "\xe90f" )
( "clojure" . "\xe919" )
( "clojure-line" . "\xe91a" )
( "coffeescript" . "\xe914" )
( "cplusplus" . "\xe913" )
( "cplusplus-line" . "\xe910" )
( "csharp" . "\xe911" )
( "csharp-line" . "\xe912" )
( "css3" . "\xe91b" )
( "css3-alt" . "\xe91c" )
( "d3" . "\xe90e" )
( "dlang" . "\xe935" )
( "elixir" . "\xe936" )
( "erlang" . "\xe934" )
( "git" . "\xe907" )
( "go" . "\xe91d" )
( "google-drive" . "\xe91e" )
( "grunt" . "\xe90d" )
( "grunt-line" . "\xe91f" )
( "gulp" . "\xe920" )
( "haskell" . "\xe921" )
( "html5" . "\xe932" )
( "jasmine" . "\xe904" )
( "java" . "\xe922" )
( "javascript" . "\xe906" )
( "javascript-badge" . "\xe923" )
( "javascript-shield" . "\xe924" )
( "less" . "\xe90b" )
( "nginx" . "\xe933" )
( "nodejs" . "\xe925" )
( "perl" . "\xe905" )
( "perldocs" . "\xe926" )
( "postgresql" . "\xe938" )
( "prolog" . "\xe927" )
( "python" . "\xe928" )
( "react" . "\xe929" )
( "ruby" . "\xe92a" )
( "ruby-alt" . "\xe92b" )
( "rust" . "\xe92c" )
( "sass" . "\xe92d" )
( "scala" . "\xe908" )
( "script" . "\xe90a" )
( "spring" . "\xe937" )
( "stylus" . "\xe92e" )
( "svg" . "\xe903" )
( "swift" . "\xe92f" )
( "terminal" . "\xe930" )
( "terminal-alt" . "\xe931" )
( "battery-charging" . "\xe939" )
( "arrow-left" . "\xe93a" )
( "arrow-right" . "\xe93b" )
( "cup-left" . "\xe93c" )
( "cup-right" . "\xe93d" )
( "slant-left" . "\xe93e" )
( "slant-right" . "\xe93f" )
( "wave-left" . "\xe940" )
( "wave-right" . "\xe941" )
))
(provide 'data-alltheicons)

View file

@ -0,0 +1,641 @@
(defvar all-the-icons-data/fa-icon-alist
'(
("500px" . "\xf26e")
("adjust" . "\xf042")
("adn" . "\xf170")
("align-center" . "\xf037")
("align-justify" . "\xf039")
("align-left" . "\xf036")
("align-right" . "\xf038")
("amazon" . "\xf270")
("ambulance" . "\xf0f9")
("american-sign-language-interpreting" . "\xf2a3")
("anchor" . "\xf13d")
("android" . "\xf17b")
("angellist" . "\xf209")
("angle-double-down" . "\xf103")
("angle-double-left" . "\xf100")
("angle-double-right" . "\xf101")
("angle-double-up" . "\xf102")
("angle-down" . "\xf107")
("angle-left" . "\xf104")
("angle-right" . "\xf105")
("angle-up" . "\xf106")
("apple" . "\xf179")
("archive" . "\xf187")
("area-chart" . "\xf1fe")
("arrow-circle-down" . "\xf0ab")
("arrow-circle-left" . "\xf0a8")
("arrow-circle-o-down" . "\xf01a")
("arrow-circle-o-left" . "\xf190")
("arrow-circle-o-right" . "\xf18e")
("arrow-circle-o-up" . "\xf01b")
("arrow-circle-right" . "\xf0a9")
("arrow-circle-up" . "\xf0aa")
("arrow-down" . "\xf063")
("arrow-left" . "\xf060")
("arrow-right" . "\xf061")
("arrow-up" . "\xf062")
("arrows" . "\xf047")
("arrows-alt" . "\xf0b2")
("arrows-h" . "\xf07e")
("arrows-v" . "\xf07d")
("assistive-listening-systems" . "\xf2a2")
("asterisk" . "\xf069")
("at" . "\xf1fa")
("audio-description" . "\xf29e")
("backward" . "\xf04a")
("balance-scale" . "\xf24e")
("ban" . "\xf05e")
("bar-chart" . "\xf080")
("barcode" . "\xf02a")
("bars" . "\xf0c9")
("battery-empty" . "\xf244")
("battery-full" . "\xf240")
("battery-half" . "\xf242")
("battery-quarter" . "\xf243")
("battery-three-quarters" . "\xf241")
("bed" . "\xf236")
("beer" . "\xf0fc")
("behance" . "\xf1b4")
("behance-square" . "\xf1b5")
("bell" . "\xf0f3")
("bell-o" . "\xf0a2")
("bell-slash" . "\xf1f6")
("bell-slash-o" . "\xf1f7")
("bicycle" . "\xf206")
("binoculars" . "\xf1e5")
("birthday-cake" . "\xf1fd")
("bitbucket" . "\xf171")
("bitbucket-square" . "\xf172")
("black-tie" . "\xf27e")
("blind" . "\xf29d")
("bluetooth" . "\xf293")
("bluetooth-b" . "\xf294")
("bold" . "\xf032")
("bolt" . "\xf0e7")
("bomb" . "\xf1e2")
("book" . "\xf02d")
("bookmark" . "\xf02e")
("bookmark-o" . "\xf097")
("braille" . "\xf2a1")
("briefcase" . "\xf0b1")
("btc" . "\xf15a")
("bug" . "\xf188")
("building" . "\xf1ad")
("building-o" . "\xf0f7")
("bullhorn" . "\xf0a1")
("bullseye" . "\xf140")
("bus" . "\xf207")
("buysellads" . "\xf20d")
("calculator" . "\xf1ec")
("calendar" . "\xf073")
("calendar-check-o" . "\xf274")
("calendar-minus-o" . "\xf272")
("calendar-o" . "\xf133")
("calendar-plus-o" . "\xf271")
("calendar-times-o" . "\xf273")
("camera" . "\xf030")
("camera-retro" . "\xf083")
("car" . "\xf1b9")
("caret-down" . "\xf0d7")
("caret-left" . "\xf0d9")
("caret-right" . "\xf0da")
("caret-square-o-down" . "\xf150")
("caret-square-o-left" . "\xf191")
("caret-square-o-right" . "\xf152")
("caret-square-o-up" . "\xf151")
("caret-up" . "\xf0d8")
("cart-arrow-down" . "\xf218")
("cart-plus" . "\xf217")
("cc" . "\xf20a")
("cc-amex" . "\xf1f3")
("cc-diners-club" . "\xf24c")
("cc-discover" . "\xf1f2")
("cc-jcb" . "\xf24b")
("cc-mastercard" . "\xf1f1")
("cc-paypal" . "\xf1f4")
("cc-stripe" . "\xf1f5")
("cc-visa" . "\xf1f0")
("certificate" . "\xf0a3")
("chain-broken" . "\xf127")
("check" . "\xf00c")
("check-circle" . "\xf058")
("check-circle-o" . "\xf05d")
("check-square" . "\xf14a")
("check-square-o" . "\xf046")
("chevron-circle-down" . "\xf13a")
("chevron-circle-left" . "\xf137")
("chevron-circle-right" . "\xf138")
("chevron-circle-up" . "\xf139")
("chevron-down" . "\xf078")
("chevron-left" . "\xf053")
("chevron-right" . "\xf054")
("chevron-up" . "\xf077")
("child" . "\xf1ae")
("chrome" . "\xf268")
("circle" . "\xf111")
("circle-o" . "\xf10c")
("circle-o-notch" . "\xf1ce")
("circle-thin" . "\xf1db")
("clipboard" . "\xf0ea")
("clock-o" . "\xf017")
("clone" . "\xf24d")
("cloud" . "\xf0c2")
("cloud-download" . "\xf0ed")
("cloud-upload" . "\xf0ee")
("code" . "\xf121")
("code-fork" . "\xf126")
("codepen" . "\xf1cb")
("codiepie" . "\xf284")
("coffee" . "\xf0f4")
("cog" . "\xf013")
("cogs" . "\xf085")
("columns" . "\xf0db")
("comment" . "\xf075")
("comment-o" . "\xf0e5")
("commenting" . "\xf27a")
("commenting-o" . "\xf27b")
("comments" . "\xf086")
("comments-o" . "\xf0e6")
("compass" . "\xf14e")
("compress" . "\xf066")
("connectdevelop" . "\xf20e")
("contao" . "\xf26d")
("copyright" . "\xf1f9")
("creative-commons" . "\xf25e")
("credit-card" . "\xf09d")
("credit-card-alt" . "\xf283")
("crop" . "\xf125")
("crosshairs" . "\xf05b")
("css3" . "\xf13c")
("cube" . "\xf1b2")
("cubes" . "\xf1b3")
("cutlery" . "\xf0f5")
("dashcube" . "\xf210")
("database" . "\xf1c0")
("deaf" . "\xf2a4")
("delicious" . "\xf1a5")
("desktop" . "\xf108")
("deviantart" . "\xf1bd")
("diamond" . "\xf219")
("digg" . "\xf1a6")
("dot-circle-o" . "\xf192")
("download" . "\xf019")
("dribbble" . "\xf17d")
("dropbox" . "\xf16b")
("drupal" . "\xf1a9")
("edge" . "\xf282")
("eject" . "\xf052")
("ellipsis-h" . "\xf141")
("ellipsis-v" . "\xf142")
("empire" . "\xf1d1")
("envelope" . "\xf0e0")
("envelope-o" . "\xf003")
("envelope-square" . "\xf199")
("envira" . "\xf299")
("eraser" . "\xf12d")
("eur" . "\xf153")
("exchange" . "\xf0ec")
("exclamation" . "\xf12a")
("exclamation-circle" . "\xf06a")
("exclamation-triangle" . "\xf071")
("expand" . "\xf065")
("expeditedssl" . "\xf23e")
("external-link" . "\xf08e")
("external-link-square" . "\xf14c")
("eye" . "\xf06e")
("eye-slash" . "\xf070")
("eyedropper" . "\xf1fb")
("facebook" . "\xf09a")
("facebook-official" . "\xf230")
("facebook-square" . "\xf082")
("fast-backward" . "\xf049")
("fast-forward" . "\xf050")
("fax" . "\xf1ac")
("female" . "\xf182")
("fighter-jet" . "\xf0fb")
("file" . "\xf15b")
("file-archive-o" . "\xf1c6")
("file-audio-o" . "\xf1c7")
("file-code-o" . "\xf1c9")
("file-excel-o" . "\xf1c3")
("file-image-o" . "\xf1c5")
("file-o" . "\xf016")
("file-pdf-o" . "\xf1c1")
("file-powerpoint-o" . "\xf1c4")
("file-text" . "\xf15c")
("file-text-o" . "\xf0f6")
("file-video-o" . "\xf1c8")
("file-word-o" . "\xf1c2")
("files-o" . "\xf0c5")
("film" . "\xf008")
("filter" . "\xf0b0")
("fire" . "\xf06d")
("fire-extinguisher" . "\xf134")
("firefox" . "\xf269")
("first-order" . "\xf2b0")
("flag" . "\xf024")
("flag-checkered" . "\xf11e")
("flag-o" . "\xf11d")
("flask" . "\xf0c3")
("flickr" . "\xf16e")
("floppy-o" . "\xf0c7")
("folder" . "\xf07b")
("folder-o" . "\xf114")
("folder-open" . "\xf07c")
("folder-open-o" . "\xf115")
("font" . "\xf031")
("font-awesome" . "\xf2b4")
("fonticons" . "\xf280")
("fort-awesome" . "\xf286")
("forumbee" . "\xf211")
("forward" . "\xf04e")
("foursquare" . "\xf180")
("frown-o" . "\xf119")
("futbol-o" . "\xf1e3")
("gamepad" . "\xf11b")
("gavel" . "\xf0e3")
("gbp" . "\xf154")
("genderless" . "\xf22d")
("get-pocket" . "\xf265")
("gg" . "\xf260")
("gg-circle" . "\xf261")
("gift" . "\xf06b")
("git" . "\xf1d3")
("git-square" . "\xf1d2")
("github" . "\xf09b")
("github-alt" . "\xf113")
("github-square" . "\xf092")
("gitlab" . "\xf296")
("glass" . "\xf000")
("glide" . "\xf2a5")
("glide-g" . "\xf2a6")
("globe" . "\xf0ac")
("google" . "\xf1a0")
("google-plus" . "\xf0d5")
("google-plus-official" . "\xf2b3")
("google-plus-square" . "\xf0d4")
("google-wallet" . "\xf1ee")
("graduation-cap" . "\xf19d")
("gratipay" . "\xf184")
("h-square" . "\xf0fd")
("hacker-news" . "\xf1d4")
("hand-lizard-o" . "\xf258")
("hand-o-down" . "\xf0a7")
("hand-o-left" . "\xf0a5")
("hand-o-right" . "\xf0a4")
("hand-o-up" . "\xf0a6")
("hand-paper-o" . "\xf256")
("hand-peace-o" . "\xf25b")
("hand-pointer-o" . "\xf25a")
("hand-rock-o" . "\xf255")
("hand-scissors-o" . "\xf257")
("hand-spock-o" . "\xf259")
("hashtag" . "\xf292")
("hdd-o" . "\xf0a0")
("header" . "\xf1dc")
("headphones" . "\xf025")
("heart" . "\xf004")
("heart-o" . "\xf08a")
("heartbeat" . "\xf21e")
("history" . "\xf1da")
("home" . "\xf015")
("hospital-o" . "\xf0f8")
("hourglass" . "\xf254")
("hourglass-end" . "\xf253")
("hourglass-half" . "\xf252")
("hourglass-o" . "\xf250")
("hourglass-start" . "\xf251")
("houzz" . "\xf27c")
("html5" . "\xf13b")
("i-cursor" . "\xf246")
("ils" . "\xf20b")
("inbox" . "\xf01c")
("indent" . "\xf03c")
("industry" . "\xf275")
("info" . "\xf129")
("info-circle" . "\xf05a")
("inr" . "\xf156")
("instagram" . "\xf16d")
("internet-explorer" . "\xf26b")
("ioxhost" . "\xf208")
("italic" . "\xf033")
("joomla" . "\xf1aa")
("jpy" . "\xf157")
("jsfiddle" . "\xf1cc")
("key" . "\xf084")
("keyboard-o" . "\xf11c")
("krw" . "\xf159")
("language" . "\xf1ab")
("laptop" . "\xf109")
("lastfm" . "\xf202")
("lastfm-square" . "\xf203")
("leaf" . "\xf06c")
("leanpub" . "\xf212")
("lemon-o" . "\xf094")
("level-down" . "\xf149")
("level-up" . "\xf148")
("life-ring" . "\xf1cd")
("lightbulb-o" . "\xf0eb")
("line-chart" . "\xf201")
("link" . "\xf0c1")
("linkedin" . "\xf0e1")
("linkedin-square" . "\xf08c")
("linux" . "\xf17c")
("list" . "\xf03a")
("list-alt" . "\xf022")
("list-ol" . "\xf0cb")
("list-ul" . "\xf0ca")
("location-arrow" . "\xf124")
("lock" . "\xf023")
("long-arrow-down" . "\xf175")
("long-arrow-left" . "\xf177")
("long-arrow-right" . "\xf178")
("long-arrow-up" . "\xf176")
("low-vision" . "\xf2a8")
("magic" . "\xf0d0")
("magnet" . "\xf076")
("male" . "\xf183")
("map" . "\xf279")
("map-marker" . "\xf041")
("map-o" . "\xf278")
("map-pin" . "\xf276")
("map-signs" . "\xf277")
("mars" . "\xf222")
("mars-double" . "\xf227")
("mars-stroke" . "\xf229")
("mars-stroke-h" . "\xf22b")
("mars-stroke-v" . "\xf22a")
("maxcdn" . "\xf136")
("meanpath" . "\xf20c")
("medium" . "\xf23a")
("medkit" . "\xf0fa")
("meh-o" . "\xf11a")
("mercury" . "\xf223")
("microphone" . "\xf130")
("microphone-slash" . "\xf131")
("minus" . "\xf068")
("minus-circle" . "\xf056")
("minus-square" . "\xf146")
("minus-square-o" . "\xf147")
("mixcloud" . "\xf289")
("mobile" . "\xf10b")
("modx" . "\xf285")
("money" . "\xf0d6")
("moon-o" . "\xf186")
("motorcycle" . "\xf21c")
("mouse-pointer" . "\xf245")
("music" . "\xf001")
("neuter" . "\xf22c")
("newspaper-o" . "\xf1ea")
("object-group" . "\xf247")
("object-ungroup" . "\xf248")
("odnoklassniki" . "\xf263")
("odnoklassniki-square" . "\xf264")
("opencart" . "\xf23d")
("openid" . "\xf19b")
("opera" . "\xf26a")
("optin-monster" . "\xf23c")
("outdent" . "\xf03b")
("pagelines" . "\xf18c")
("paint-brush" . "\xf1fc")
("paper-plane" . "\xf1d8")
("paper-plane-o" . "\xf1d9")
("paperclip" . "\xf0c6")
("paragraph" . "\xf1dd")
("pause" . "\xf04c")
("pause-circle" . "\xf28b")
("pause-circle-o" . "\xf28c")
("paw" . "\xf1b0")
("paypal" . "\xf1ed")
("pencil" . "\xf040")
("pencil-square" . "\xf14b")
("pencil-square-o" . "\xf044")
("percent" . "\xf295")
("phone" . "\xf095")
("phone-square" . "\xf098")
("picture-o" . "\xf03e")
("pie-chart" . "\xf200")
("pied-piper" . "\xf2ae")
("pied-piper-alt" . "\xf1a8")
("pied-piper-pp" . "\xf1a7")
("pinterest" . "\xf0d2")
("pinterest-p" . "\xf231")
("pinterest-square" . "\xf0d3")
("plane" . "\xf072")
("play" . "\xf04b")
("play-circle" . "\xf144")
("play-circle-o" . "\xf01d")
("plug" . "\xf1e6")
("plus" . "\xf067")
("plus-circle" . "\xf055")
("plus-square" . "\xf0fe")
("plus-square-o" . "\xf196")
("power-off" . "\xf011")
("print" . "\xf02f")
("product-hunt" . "\xf288")
("puzzle-piece" . "\xf12e")
("qq" . "\xf1d6")
("qrcode" . "\xf029")
("question" . "\xf128")
("question-circle" . "\xf059")
("question-circle-o" . "\xf29c")
("quote-left" . "\xf10d")
("quote-right" . "\xf10e")
("random" . "\xf074")
("rebel" . "\xf1d0")
("recycle" . "\xf1b8")
("reddit" . "\xf1a1")
("reddit-alien" . "\xf281")
("reddit-square" . "\xf1a2")
("refresh" . "\xf021")
("registered" . "\xf25d")
("renren" . "\xf18b")
("repeat" . "\xf01e")
("reply" . "\xf112")
("reply-all" . "\xf122")
("retweet" . "\xf079")
("road" . "\xf018")
("rocket" . "\xf135")
("rss" . "\xf09e")
("rss-square" . "\xf143")
("rub" . "\xf158")
("safari" . "\xf267")
("scissors" . "\xf0c4")
("scribd" . "\xf28a")
("search" . "\xf002")
("search-minus" . "\xf010")
("search-plus" . "\xf00e")
("sellsy" . "\xf213")
("server" . "\xf233")
("share" . "\xf064")
("share-alt" . "\xf1e0")
("share-alt-square" . "\xf1e1")
("share-square" . "\xf14d")
("share-square-o" . "\xf045")
("shield" . "\xf132")
("ship" . "\xf21a")
("shirtsinbulk" . "\xf214")
("shopping-bag" . "\xf290")
("shopping-basket" . "\xf291")
("shopping-cart" . "\xf07a")
("sign-in" . "\xf090")
("sign-language" . "\xf2a7")
("sign-out" . "\xf08b")
("signal" . "\xf012")
("simplybuilt" . "\xf215")
("sitemap" . "\xf0e8")
("skyatlas" . "\xf216")
("skype" . "\xf17e")
("slack" . "\xf198")
("sliders" . "\xf1de")
("slideshare" . "\xf1e7")
("smile-o" . "\xf118")
("snapchat" . "\xf2ab")
("snapchat-ghost" . "\xf2ac")
("snapchat-square" . "\xf2ad")
("sort" . "\xf0dc")
("sort-alpha-asc" . "\xf15d")
("sort-alpha-desc" . "\xf15e")
("sort-amount-asc" . "\xf160")
("sort-amount-desc" . "\xf161")
("sort-asc" . "\xf0de")
("sort-desc" . "\xf0dd")
("sort-numeric-asc" . "\xf162")
("sort-numeric-desc" . "\xf163")
("soundcloud" . "\xf1be")
("space-shuttle" . "\xf197")
("spinner" . "\xf110")
("spoon" . "\xf1b1")
("spotify" . "\xf1bc")
("square" . "\xf0c8")
("square-o" . "\xf096")
("stack-exchange" . "\xf18d")
("stack-overflow" . "\xf16c")
("star" . "\xf005")
("star-half" . "\xf089")
("star-half-o" . "\xf123")
("star-o" . "\xf006")
("steam" . "\xf1b6")
("steam-square" . "\xf1b7")
("step-backward" . "\xf048")
("step-forward" . "\xf051")
("stethoscope" . "\xf0f1")
("sticky-note" . "\xf249")
("sticky-note-o" . "\xf24a")
("stop" . "\xf04d")
("stop-circle" . "\xf28d")
("stop-circle-o" . "\xf28e")
("street-view" . "\xf21d")
("strikethrough" . "\xf0cc")
("stumbleupon" . "\xf1a4")
("stumbleupon-circle" . "\xf1a3")
("subscript" . "\xf12c")
("subway" . "\xf239")
("suitcase" . "\xf0f2")
("sun-o" . "\xf185")
("superscript" . "\xf12b")
("table" . "\xf0ce")
("tablet" . "\xf10a")
("tachometer" . "\xf0e4")
("tag" . "\xf02b")
("tags" . "\xf02c")
("tasks" . "\xf0ae")
("taxi" . "\xf1ba")
("television" . "\xf26c")
("tencent-weibo" . "\xf1d5")
("terminal" . "\xf120")
("text-height" . "\xf034")
("text-width" . "\xf035")
("th" . "\xf00a")
("th-large" . "\xf009")
("th-list" . "\xf00b")
("themeisle" . "\xf2b2")
("thumb-tack" . "\xf08d")
("thumbs-down" . "\xf165")
("thumbs-o-down" . "\xf088")
("thumbs-o-up" . "\xf087")
("thumbs-up" . "\xf164")
("ticket" . "\xf145")
("times" . "\xf00d")
("times-circle" . "\xf057")
("times-circle-o" . "\xf05c")
("tint" . "\xf043")
("toggle-off" . "\xf204")
("toggle-on" . "\xf205")
("trademark" . "\xf25c")
("train" . "\xf238")
("transgender" . "\xf224")
("transgender-alt" . "\xf225")
("trash" . "\xf1f8")
("trash-o" . "\xf014")
("tree" . "\xf1bb")
("trello" . "\xf181")
("tripadvisor" . "\xf262")
("trophy" . "\xf091")
("truck" . "\xf0d1")
("try" . "\xf195")
("tty" . "\xf1e4")
("tumblr" . "\xf173")
("tumblr-square" . "\xf174")
("twitch" . "\xf1e8")
("twitter" . "\xf099")
("twitter-square" . "\xf081")
("umbrella" . "\xf0e9")
("underline" . "\xf0cd")
("undo" . "\xf0e2")
("universal-access" . "\xf29a")
("university" . "\xf19c")
("unlock" . "\xf09c")
("unlock-alt" . "\xf13e")
("upload" . "\xf093")
("usb" . "\xf287")
("usd" . "\xf155")
("user" . "\xf007")
("user-md" . "\xf0f0")
("user-plus" . "\xf234")
("user-secret" . "\xf21b")
("user-times" . "\xf235")
("users" . "\xf0c0")
("venus" . "\xf221")
("venus-double" . "\xf226")
("venus-mars" . "\xf228")
("viacoin" . "\xf237")
("viadeo" . "\xf2a9")
("viadeo-square" . "\xf2aa")
("video-camera" . "\xf03d")
("vimeo" . "\xf27d")
("vimeo-square" . "\xf194")
("vine" . "\xf1ca")
("vk" . "\xf189")
("volume-control-phone" . "\xf2a0")
("volume-down" . "\xf027")
("volume-off" . "\xf026")
("volume-up" . "\xf028")
("weibo" . "\xf18a")
("weixin" . "\xf1d7")
("whatsapp" . "\xf232")
("wheelchair" . "\xf193")
("wheelchair-alt" . "\xf29b")
("wifi" . "\xf1eb")
("wikipedia-w" . "\xf266")
("windows" . "\xf17a")
("wordpress" . "\xf19a")
("wpbeginner" . "\xf297")
("wpforms" . "\xf298")
("wrench" . "\xf0ad")
("xing" . "\xf168")
("xing-square" . "\xf169")
("y-combinator" . "\xf23b")
("yahoo" . "\xf19e")
("yelp" . "\xf1e9")
("yoast" . "\xf2b1")
("youtube" . "\xf167")
("youtube-play" . "\xf16a")
("youtube-square" . "\xf166")
))
(provide 'data-faicons)

View file

@ -0,0 +1,502 @@
(defvar all-the-icons-data/file-icon-alist
'(
( "1c" . "\xa5ea" )
( "1c-alt" . "\xea28" )
( "MJML" . "\xea6f" )
( "R" . "\xe905" )
( "abap" . "\xe92b" )
( "abif" . "\xea4e" )
( "access" . "\xe9ea" )
( "actionscript" . "\xe92e" )
( "ada" . "\xe90b" )
( "ae" . "\xe9f3" )
( "ai" . "\xe6b4" )
( "akka" . "\xea0e" )
( "alex" . "\x29cb" )
( "alloy" . "\xe935" )
( "alpine-linux" . "\xe9ff" )
( "ampl" . "\xe94e" )
( "amx" . "\xe99b" )
( "angelscript" . "\xea5b" )
( "ansible" . "\x24b6" )
( "ansible-alt" . "\x61" )
( "ant" . "\xe93e" )
( "antlr" . "\xe92c" )
( "antwar" . "\x2591" )
( "api-blueprint" . "\xe92d" )
( "apl" . "\x234b" )
( "apl-old" . "\xe909" )
( "apple" . "\xe925" )
( "appveyor" . "\xe923" )
( "arc" . "\xe92f" )
( "arch-linux" . "\x41" )
( "arduino" . "\xe930" )
( "arttext" . "\x24d0" )
( "asciidoc" . "\xe918" )
( "assembly" . "\xEB4F" )
( "ats" . "\xe934" )
( "audacity" . "\xe9f9" )
( "augeas" . "\xe931" )
( "aurelia" . "\xea48" )
( "auto-hotkey" . "\xe932" )
( "autoit" . "\xe933" )
( "babel" . "\xe91f" )
( "bazel" . "\xea5a" )
( "bem" . "\xea59" )
( "bib" . "\xe601" )
( "bintray" . "\xea6e" )
( "bithound" . "\xea2a" )
( "blender" . "\xe9fa" )
( "bluespec" . "\xe93c" )
( "boo" . "\xe939" )
( "brain" . "\xe93a" )
( "brakeman" . "\xe9d6" )
( "bro" . "\xe93b" )
( "broccoli" . "\xe922" )
( "brotli" . "\xea6c" )
( "browserslist" . "\xea80" )
( "brunch" . "\xea47" )
( "buck" . "\xea46" )
( "build-boot" . "\xf103" )
( "bundler" . "\xea45" )
( "byond" . "\xe962" )
( "cabal" . "\xe9c2" )
( "caddy" . "\xea58" )
( "cake" . "\xe9e3" )
( "cakefile" . "\xe924" )
( "cakephp" . "\xea43" )
( "cakephp-old" . "\xe9d3" )
( "cc" . "\xe9d5" )
( "ceylon" . "\xe94f" )
( "chai" . "\x63" )
( "chapel" . "\xe950" )
( "chartjs" . "\xea0b" )
( "chef" . "\xea42" )
( "chuck" . "\xe943" )
( "circle-ci" . "\xea12" )
( "cirru" . "\xe951" )
( "ckeditor" . "\xea0c" )
( "clarion" . "\xe952" )
( "clean" . "\xe95b" )
( "click" . "\xe95c" )
( "clips" . "\xe940" )
( "clj" . "\xf105" )
( "cljs" . "\xf104" )
( "closure-template" . "\xea82" )
( "cmake" . "\xe93f" )
( "cobol" . "\xea44" )
( "codecov" . "\x2602" )
( "codekit" . "\xea41" )
( "codemirror" . "\xea0d" )
( "codeship" . "\xea6a" )
( "cold-fusion" . "\xe929" )
( "clisp" . "\xe972" )
( "composer" . "\xe683" )
( "config" . "\xf07c" )
( "config-coffeescript" . "\xeb18" )
( "config-go" . "\xeb12" )
( "config-haskell" . "\xeb14" )
( "config-js" . "\xeb1a" )
( "config-perl" . "\xeb19" )
( "config-python" . "\xeb15" )
( "config-react" . "\xeb16" )
( "config-ruby" . "\xeb17" )
( "config-rust" . "\xeb13" )
( "config-typescript" . "\xeb1b" )
( "coq" . "\xe95f" )
( "cordova" . "\xea11" )
( "cp" . "\xe942" )
( "cpan" . "\xea87" )
( "creole" . "\xe95e" )
( "crystal" . "\xe902" )
( "cs-script" . "\xe9e2" )
( "csound" . "\xe9f0" )
( "cucumber" . "\xf02b" )
( "cython" . "\xe963" )
( "d3" . "\xea10" )
( "darcs" . "\xe964" )
( "dart" . "\xe698" )
( "dashboard" . "\xf07d" )
( "dbase" . "\xe9f1" )
( "default" . "\x1f5cc" )
( "delphi" . "\xea40" )
( "devicetree" . "\xea57" )
( "diff" . "\xe960" )
( "dockerfile" . "\xf106" )
( "doclets" . "\xea3f" )
( "doge" . "\xe946" )
( "dom" . "\xea71" )
( "donejs" . "\x1f3c1" )
( "doxygen" . "\xe928" )
( "dragula" . "\x1f44c" )
( "drone" . "\xea3d" )
( "dyalog" . "\xe90c" )
( "dylib" . "\xea15" )
( "e" . "\x45" )
( "eagle" . "\xe965" )
( "easybuild" . "\xea85" )
( "ec" . "\xe9c9" )
( "ecere" . "\xe966" )
( "edge" . "\xea78" )
( "editorconfig" . "\xea1b" )
( "eiffel" . "\xe967" )
( "ejs" . "\xea4b" )
( "electron" . "\xea27" )
( "elm" . "\xf102" )
( "emacs" . "\xe926" )
( "elisp" . "\xe926" )
( "ember" . "\xe61b" )
( "emberscript" . "\xe968" )
( "eq" . "\xea0a" )
( "esdoc" . "\xea5c" )
( "eslint" . "\xea0f" )
( "eslint-old" . "\xe90e" )
( "excel" . "\xe9ee" )
( "fabfile" . "\xe94b" )
( "factor" . "\xe96a" )
( "fancy" . "\xe96b" )
( "fantom" . "\xe96f" )
( "fbx" . "\xe9fc" )
( "ffmpeg" . "\xea22" )
( "finder" . "\xe9e9" )
( "firebase" . "\xea7f" )
( "flow" . "\xe921" )
( "flux" . "\xe969" )
( "font" . "\xe90f" )
( "fontforge" . "\xfb00" )
( "fortran" . "\xe90a" )
( "franca" . "\xea56" )
( "freemarker" . "\xe970" )
( "frege" . "\xe96e" )
( "fsharp" . "\xe6a7" )
( "fuel-ux" . "\xea09" )
( "gams" . "\xe973" )
( "gap" . "\xe971" )
( "gdb" . "\xea08" )
( "genshi" . "\xe976" )
( "gentoo" . "\xe96d" )
( "gf" . "\xe978" )
( "gitlab" . "\xea3c" )
( "glade" . "\xe938" )
( "glyphs" . "\x47" )
( "gn" . "\xea25" )
( "gnu" . "\xe679" )
( "go" . "\xeaae" )
( "godot" . "\xe974" )
( "golo" . "\xe979" )
( "gosu" . "\xe97a" )
( "gradle" . "\xe903" )
( "graphql" . "\xe97c" )
( "graphviz" . "\xe97d" )
( "groovy" . "\xe904" )
( "grunt" . "\xe611" )
( "gulp" . "\xe610" )
( "hack" . "\xe9ce" )
( "haml" . "\xf15b" )
( "harbour" . "\xe97b" )
( "hashicorp" . "\xe97e" )
( "haxe" . "\xe907" )
( "haxedevelop" . "\xea3b" )
( "hg" . "\x263f" )
( "hoplon" . "\xea4d" )
( "hy" . "\xe97f" )
( "icu" . "\xea23" )
( "id" . "\xe9f4" )
( "idl" . "\xe947" )
( "idris" . "\xe983" )
( "igorpro" . "\xe980" )
( "image" . "\xf012" )
( "inform7" . "\xe984" )
( "inno" . "\xe985" )
( "io" . "\xe981" )
( "ioke" . "\xe982" )
( "ionic-project" . "\xf14b" )
( "isabelle" . "\xe945" )
( "j" . "\xe937" )
( "jade" . "\xe90d" )
( "jake" . "\xe948" )
( "jasmine" . "\xea3a" )
( "jenkins" . "\xe667" )
( "jest" . "\xea39" )
( "jinja" . "\xe944" )
( "jison" . "\xea55" )
( "jolie" . "\xea75" )
( "jsonld" . "\xe958" )
( "jsx" . "\xf100" )
( "jsx-2" . "\xf101" )
( "jsx2-alt" . "\xe9e6" )
( "julia" . "\x26ec" )
( "junos" . "\xea81" )
( "jupyter" . "\xe987" )
( "karma" . "\xe9cd" )
( "keynote" . "\xe9e5" )
( "khronos" . "\xe9f8" )
( "kicad" . "\xea4c" )
( "kitchenci" . "\xea38" )
( "kivy" . "\xe901" )
( "knockout" . "\x4b" )
( "kotlin" . "\xe989" )
( "krl" . "\xe988" )
( "labview" . "\xe98a" )
( "lasso" . "\xe98c" )
( "leaflet" . "\xea07" )
( "lean" . "\x4c" )
( "lerna" . "\xea37" )
( "lfe" . "\xe94c" )
( "libuv" . "\xea21" )
( "lightwave" . "\xe9fb" )
( "lime" . "\xea36" )
( "lisp" . "\xe908" )
( "livescript" . "\xe914" )
( "llvm" . "\xe91d" )
( "logtalk" . "\xe98d" )
( "lookml" . "\xe98e" )
( "lsl" . "\xe98b" )
( "lua" . "\xe91b" )
( "mako" . "\xe98f" )
( "man-page" . "\xe936" )
( "mapbox" . "\xe941" )
( "markdownlint" . "\xf0c9" )
( "marko" . "\xe920" )
( "mathematica" . "\xe990" )
( "mathjax" . "\xea06" )
( "matlab" . "\xe991" )
( "max" . "\xe993" )
( "maxscript" . "\xe900" )
( "maya" . "\xe9f6" )
( "mediawiki" . "\xe954" )
( "mercury" . "\xe994" )
( "meson" . "\xeafe" )
( "metal" . "\x4d" )
( "meteor" . "\xe6a5" )
( "microsoft-infopath" . "\xea35" )
( "minecraft" . "\xe9dc" )
( "minizinc" . "\xea53" )
( "mirah" . "\xe995" )
( "miranda" . "\xea52" )
( "mocha" . "\x26fe" )
( "modula-2" . "\xe996" )
( "moment" . "\x1f558" )
( "moment-tz" . "\x1f30d" )
( "monkey" . "\xe997" )
( "moustache" . "\xe60f" )
( "mruby" . "\xea18" )
( "mupad" . "\xe9ca" )
( "nano" . "\xea76" )
( "nanoc" . "\xea51" )
( "nant" . "\xe9e1" )
( "nasm" . "\xea72" )
( "neko" . "\xea05" )
( "netlogo" . "\xe99c" )
( "new-relic" . "\xe9d7" )
( "nginx" . "\xf146b" )
( "nib" . "\x2712" )
( "nimrod" . "\xe998" )
( "nit" . "\xe999" )
( "nix" . "\xe99a" )
( "nmap" . "\xe94d" )
( "nodemon" . "\xea26" )
( "normalize" . "\xea04" )
( "npm" . "\xe91c" )
( "npm-old" . "\xf17b" )
( "nsis" . "\xea1e" )
( "nsis-old" . "\xe992" )
( "nuclide" . "\xea34" )
( "nuget" . "\xe9d9" )
( "numpy" . "\xe99d" )
( "nunjucks" . "\xe953" )
( "nvidia" . "\xe95d" )
( "nxc" . "\xea6b" )
( "obj" . "\xe9e8" )
( "objective-j" . "\xe99e" )
( "ocaml" . "\xe91a" )
( "octave" . "\xea33" )
( "odin" . "\xeb36" )
( "onenote" . "\xe9eb" )
( "ooc" . "\xe9cb" )
( "opa" . "\x2601" )
( "opencl" . "\xe99f" )
( "opengl" . "\xea7a" )
( "openoffice" . "\xe9e4" )
( "openscad" . "\xe911" )
( "org" . "\xe917" )
( "owl" . "\xe957" )
( "ox" . "\xe9a1" )
( "oxygene" . "\xe9bf" )
( "oz" . "\xe9be" )
( "p4" . "\xea50" )
( "pan" . "\xe9bd" )
( "papyrus" . "\xe9bc" )
( "parrot" . "\xe9bb" )
( "pascal" . "\xe92a" )
( "patch" . "\xe961" )
( "pawn" . "\x265f" )
( "pb" . "\xea14" )
( "pegjs" . "\xea74" )
( "raku" . "\xe96c" )
( "phalcon" . "\xe94a" )
( "phoenix" . "\xea5f" )
( "php" . "\xf147" )
( "phpunit" . "\xea32" )
( "pickle" . "\xe9c4" )
( "pike" . "\xe9b9" )
( "platformio" . "\xea2c" )
( "pm2" . "\x2630" )
( "pod" . "\xea84" )
( "pogo" . "\xe9b8" )
( "pointwise" . "\xe977" )
( "polymer" . "\xea2b" )
( "pony" . "\xe9b7" )
( "postcss" . "\xe910" )
( "postscript" . "\xe955" )
( "povray" . "\x50" )
( "powerpoint" . "\xe9ec" )
( "powershell" . "\xe9da" )
( "precision" . "\x2295" )
( "premiere" . "\xe9f5" )
( "processing" . "\xe9a0" )
( "progress" . "\xe9c0" )
( "propeller" . "\xe9b5" )
( "proselint" . "\xea6d" )
( "protractor" . "\xe9de" )
( "ps" . "\xe6b8" )
( "pug" . "\xea13" )
( "pug-alt" . "\xe9d0" )
( "puppet" . "\xf0c3" )
( "purebasic" . "\x1b5" )
( "purescript" . "\xe9b2" )
( "racket" . "\xe9b1" )
( "raml" . "\xe913" )
( "rascal" . "\xea24" )
( "rdoc" . "\xe9b0" )
( "realbasic" . "\xe9af" )
( "reason" . "\xea1d" )
( "rebol" . "\xe9ae" )
( "red" . "\xe9ad" )
( "redux" . "\xea30" )
( "regex" . "\x2a" )
( "rexx" . "\xea16" )
( "rhino" . "\xea4a" )
( "ring" . "\x1f48d" )
( "riot" . "\xe919" )
( "robot" . "\xe9ac" )
( "rollup" . "\xea20" )
( "rollup-old" . "\xe9fd" )
( "rot" . "\x1f764" )
( "rspec" . "\xea31" )
( "rst" . "\xe9cc" )
( "sage" . "\xe9ab" )
( "saltstack" . "\xe915" )
( "sas" . "\xe95a" )
( "sbt" . "\xe9d2" )
( "sc" . "\xe9a2" )
( "scheme" . "\x3bb" )
( "scilab" . "\xe9a9" )
( "scrutinizer" . "\xe9d4" )
( "self" . "\xe9a8" )
( "sequelize" . "\xea2f" )
( "sf" . "\xe9db" )
( "shen" . "\xe9a7" )
( "shipit" . "\x26f5" )
( "shippable" . "\xea2d" )
( "shopify" . "\xe9cf" )
( "shuriken" . "\x272b" )
( "silverstripe" . "\xe800" )
( "sinatra" . "\xea03" )
( "sketch" . "\xe927" )
( "sketchup-layout" . "\xea7c" )
( "sketchup-make" . "\xea7e" )
( "sketchup-stylebuilder" . "\xea7d" )
( "slash" . "\xe9a6" )
( "snyk" . "\xea1c" )
( "solidity" . "\xea86" )
( "sparql" . "\xe959" )
( "spray" . "\xea02" )
( "sqf" . "\xe9a5" )
( "sqlite" . "\xe9dd" )
( "squarespace" . "\xea5e" )
( "stan" . "\xe9a4" )
( "stata" . "\xe9a3" )
( "storyist" . "\xe9ef" )
( "strings" . "\xe9e0" )
( "stylelint" . "\xe93d" )
( "stylus" . "\x73" )
( "stylus-full" . "\xe9f7" )
( "stylus-orb" . "\x53" )
( "sublime" . "\xe986" )
( "sv" . "\xe9c3" )
( "svelte" . "\x33dc" )
( "svn" . "\xea17" )
( "swagger" . "\xea29" )
( "tag" . "\xf015" )
( "tcl" . "\xe956" )
( "telegram" . "\x2708" )
( "terminal" . "\xf0c8" )
( "tern" . "\x1f54a" )
( "terraform" . "\xe916" )
( "test-coffeescript" . "\xea62" )
( "test-dir" . "\xea60" )
( "test-generic" . "\xea63" )
( "test-js" . "\xea64" )
( "test-perl" . "\xea65" )
( "test-python" . "\xea66" )
( "test-react" . "\xea67" )
( "test-ruby" . "\xea68" )
( "test-typescript" . "\xea69" )
( "tex" . "\xe600" )
( "textile" . "\x74" )
( "textmate" . "\x2122" )
( "thor" . "\xe9d8" )
( "tinymce" . "\xea01" )
( "tsx" . "\xe9d1" )
( "tsx-alt" . "\xe9e7" )
( "tt" . "\x54" )
( "turing" . "\xe9b6" )
( "twig" . "\x2e19" )
( "twine" . "\xea5d" )
( "txl" . "\xe9c1" )
( "typedoc" . "\xe9fe" )
( "typescript" . "\xe912" )
( "typescript-alt" . "\x2a6" )
( "typings" . "\xe9df" )
( "uno" . "\xe9b3" )
( "unreal" . "\x75" )
( "urweb" . "\xe9ba" )
( "v8" . "\xea1f" )
( "vagrant" . "\x56" )
( "vcl" . "\xe9b4" )
( "verilog" . "\xe949" )
( "vertex-shader" . "\xea79" )
( "vhdl" . "\xe9aa" )
( "video" . "\xf057" )
( "virtualbox" . "\xea3e" )
( "virtualbox-alt" . "\xea2e" )
( "visio" . "\xea83" )
( "vmware" . "\xea49" )
( "vue" . "\xe906" )
( "wasm" . "\xea70" )
( "watchman" . "\xea4f" )
( "webgl" . "\xea7b" )
( "webpack" . "\xea61" )
( "webpack-old" . "\xe91e" )
( "wercker" . "\xea19" )
( "word" . "\xe9ed" )
( "x10" . "\x2169" )
( "xamarin" . "\xea77" )
( "xmos" . "\x58" )
( "xpages" . "\xe9c5" )
( "xtend" . "\xe9c6" )
( "yarn" . "\xea1a" )
( "yasm" . "\xea73" )
( "yin-yang" . "\x262f" )
( "yoyo" . "\xe975" )
( "yui" . "\xea00" )
( "zbrush" . "\xe9f2" )
( "zephir" . "\xe9c7" )
( "zig" . "\x7A" )
( "zimpl" . "\xe9c8" )
)
)
(provide 'data-fileicons)

View file

@ -0,0 +1,935 @@
(defvar all-the-icons-data/material-icons-alist
'(("3d_rotation" . "\xe84d")
("ac_unit" . "\xeb3b")
("access_alarm" . "\xe190")
("access_alarms" . "\xe191")
("access_time" . "\xe192")
("accessibility" . "\xe84e")
("accessible" . "\xe914")
("account_balance" . "\xe84f")
("account_balance_wallet" . "\xe850")
("account_box" . "\xe851")
("account_circle" . "\xe853")
("adb" . "\xe60e")
("add" . "\xe145")
("add_a_photo" . "\xe439")
("add_alarm" . "\xe193")
("add_alert" . "\xe003")
("add_box" . "\xe146")
("add_circle" . "\xe147")
("add_circle_outline" . "\xe148")
("add_location" . "\xe567")
("add_shopping_cart" . "\xe854")
("add_to_photos" . "\xe39d")
("add_to_queue" . "\xe05c")
("adjust" . "\xe39e")
("airline_seat_flat" . "\xe630")
("airline_seat_flat_angled" . "\xe631")
("airline_seat_individual_suite" . "\xe632")
("airline_seat_legroom_extra" . "\xe633")
("airline_seat_legroom_normal" . "\xe634")
("airline_seat_legroom_reduced" . "\xe635")
("airline_seat_recline_extra" . "\xe636")
("airline_seat_recline_normal" . "\xe637")
("airplanemode_active" . "\xe195")
("airplanemode_inactive" . "\xe194")
("airplay" . "\xe055")
("airport_shuttle" . "\xeb3c")
("alarm" . "\xe855")
("alarm_add" . "\xe856")
("alarm_off" . "\xe857")
("alarm_on" . "\xe858")
("album" . "\xe019")
("all_inclusive" . "\xeb3d")
("all_out" . "\xe90b")
("android" . "\xe859")
("announcement" . "\xe85a")
("apps" . "\xe5c3")
("archive" . "\xe149")
("arrow_back" . "\xe5c4")
("arrow_downward" . "\xe5db")
("arrow_drop_down" . "\xe5c5")
("arrow_drop_down_circle" . "\xe5c6")
("arrow_drop_up" . "\xe5c7")
("arrow_forward" . "\xe5c8")
("arrow_upward" . "\xe5d8")
("art_track" . "\xe060")
("aspect_ratio" . "\xe85b")
("assessment" . "\xe85c")
("assignment" . "\xe85d")
("assignment_ind" . "\xe85e")
("assignment_late" . "\xe85f")
("assignment_return" . "\xe860")
("assignment_returned" . "\xe861")
("assignment_turned_in" . "\xe862")
("assistant" . "\xe39f")
("assistant_photo" . "\xe3a0")
("attach_file" . "\xe226")
("attach_money" . "\xe227")
("attachment" . "\xe2bc")
("audiotrack" . "\xe3a1")
("autorenew" . "\xe863")
("av_timer" . "\xe01b")
("backspace" . "\xe14a")
("backup" . "\xe864")
("battery_alert" . "\xe19c")
("battery_charging_full" . "\xe1a3")
("battery_full" . "\xe1a4")
("battery_std" . "\xe1a5")
("battery_unknown" . "\xe1a6")
("beach_access" . "\xeb3e")
("beenhere" . "\xe52d")
("block" . "\xe14b")
("bluetooth" . "\xe1a7")
("bluetooth_audio" . "\xe60f")
("bluetooth_connected" . "\xe1a8")
("bluetooth_disabled" . "\xe1a9")
("bluetooth_searching" . "\xe1aa")
("blur_circular" . "\xe3a2")
("blur_linear" . "\xe3a3")
("blur_off" . "\xe3a4")
("blur_on" . "\xe3a5")
("book" . "\xe865")
("bookmark" . "\xe866")
("bookmark_border" . "\xe867")
("border_all" . "\xe228")
("border_bottom" . "\xe229")
("border_clear" . "\xe22a")
("border_color" . "\xe22b")
("border_horizontal" . "\xe22c")
("border_inner" . "\xe22d")
("border_left" . "\xe22e")
("border_outer" . "\xe22f")
("border_right" . "\xe230")
("border_style" . "\xe231")
("border_top" . "\xe232")
("border_vertical" . "\xe233")
("branding_watermark" . "\xe06b")
("brightness_1" . "\xe3a6")
("brightness_2" . "\xe3a7")
("brightness_3" . "\xe3a8")
("brightness_4" . "\xe3a9")
("brightness_5" . "\xe3aa")
("brightness_6" . "\xe3ab")
("brightness_7" . "\xe3ac")
("brightness_auto" . "\xe1ab")
("brightness_high" . "\xe1ac")
("brightness_low" . "\xe1ad")
("brightness_medium" . "\xe1ae")
("broken_image" . "\xe3ad")
("brush" . "\xe3ae")
("bubble_chart" . "\xe6dd")
("bug_report" . "\xe868")
("build" . "\xe869")
("burst_mode" . "\xe43c")
("business" . "\xe0af")
("business_center" . "\xeb3f")
("cached" . "\xe86a")
("cake" . "\xe7e9")
("call" . "\xe0b0")
("call_end" . "\xe0b1")
("call_made" . "\xe0b2")
("call_merge" . "\xe0b3")
("call_missed" . "\xe0b4")
("call_missed_outgoing" . "\xe0e4")
("call_received" . "\xe0b5")
("call_split" . "\xe0b6")
("call_to_action" . "\xe06c")
("camera" . "\xe3af")
("camera_alt" . "\xe3b0")
("camera_enhance" . "\xe8fc")
("camera_front" . "\xe3b1")
("camera_rear" . "\xe3b2")
("camera_roll" . "\xe3b3")
("cancel" . "\xe5c9")
("card_giftcard" . "\xe8f6")
("card_membership" . "\xe8f7")
("card_travel" . "\xe8f8")
("casino" . "\xeb40")
("cast" . "\xe307")
("cast_connected" . "\xe308")
("center_focus_strong" . "\xe3b4")
("center_focus_weak" . "\xe3b5")
("change_history" . "\xe86b")
("chat" . "\xe0b7")
("chat_bubble" . "\xe0ca")
("chat_bubble_outline" . "\xe0cb")
("check" . "\xe5ca")
("check_box" . "\xe834")
("check_box_outline_blank" . "\xe835")
("check_circle" . "\xe86c")
("chevron_left" . "\xe5cb")
("chevron_right" . "\xe5cc")
("child_care" . "\xeb41")
("child_friendly" . "\xeb42")
("chrome_reader_mode" . "\xe86d")
("class" . "\xe86e")
("clear" . "\xe14c")
("clear_all" . "\xe0b8")
("close" . "\xe5cd")
("closed_caption" . "\xe01c")
("cloud" . "\xe2bd")
("cloud_circle" . "\xe2be")
("cloud_done" . "\xe2bf")
("cloud_download" . "\xe2c0")
("cloud_off" . "\xe2c1")
("cloud_queue" . "\xe2c2")
("cloud_upload" . "\xe2c3")
("code" . "\xe86f")
("collections" . "\xe3b6")
("collections_bookmark" . "\xe431")
("color_lens" . "\xe3b7")
("colorize" . "\xe3b8")
("comment" . "\xe0b9")
("compare" . "\xe3b9")
("compare_arrows" . "\xe915")
("computer" . "\xe30a")
("confirmation_number" . "\xe638")
("contact_mail" . "\xe0d0")
("contact_phone" . "\xe0cf")
("contacts" . "\xe0ba")
("content_copy" . "\xe14d")
("content_cut" . "\xe14e")
("content_paste" . "\xe14f")
("control_point" . "\xe3ba")
("control_point_duplicate" . "\xe3bb")
("copyright" . "\xe90c")
("create" . "\xe150")
("create_new_folder" . "\xe2cc")
("credit_card" . "\xe870")
("crop" . "\xe3be")
("crop_16_9" . "\xe3bc")
("crop_3_2" . "\xe3bd")
("crop_5_4" . "\xe3bf")
("crop_7_5" . "\xe3c0")
("crop_din" . "\xe3c1")
("crop_free" . "\xe3c2")
("crop_landscape" . "\xe3c3")
("crop_original" . "\xe3c4")
("crop_portrait" . "\xe3c5")
("crop_rotate" . "\xe437")
("crop_square" . "\xe3c6")
("dashboard" . "\xe871")
("data_usage" . "\xe1af")
("date_range" . "\xe916")
("dehaze" . "\xe3c7")
("delete" . "\xe872")
("delete_forever" . "\xe92b")
("delete_sweep" . "\xe16c")
("description" . "\xe873")
("desktop_mac" . "\xe30b")
("desktop_windows" . "\xe30c")
("details" . "\xe3c8")
("developer_board" . "\xe30d")
("developer_mode" . "\xe1b0")
("device_hub" . "\xe335")
("devices" . "\xe1b1")
("devices_other" . "\xe337")
("dialer_sip" . "\xe0bb")
("dialpad" . "\xe0bc")
("directions" . "\xe52e")
("directions_bike" . "\xe52f")
("directions_boat" . "\xe532")
("directions_bus" . "\xe530")
("directions_car" . "\xe531")
("directions_railway" . "\xe534")
("directions_run" . "\xe566")
("directions_subway" . "\xe533")
("directions_transit" . "\xe535")
("directions_walk" . "\xe536")
("disc_full" . "\xe610")
("dns" . "\xe875")
("do_not_disturb" . "\xe612")
("do_not_disturb_alt" . "\xe611")
("do_not_disturb_off" . "\xe643")
("do_not_disturb_on" . "\xe644")
("dock" . "\xe30e")
("domain" . "\xe7ee")
("done" . "\xe876")
("done_all" . "\xe877")
("donut_large" . "\xe917")
("donut_small" . "\xe918")
("drafts" . "\xe151")
("drag_handle" . "\xe25d")
("drive_eta" . "\xe613")
("dvr" . "\xe1b2")
("edit" . "\xe3c9")
("edit_location" . "\xe568")
("eject" . "\xe8fb")
("email" . "\xe0be")
("enhanced_encryption" . "\xe63f")
("equalizer" . "\xe01d")
("error" . "\xe000")
("error_outline" . "\xe001")
("euro_symbol" . "\xe926")
("ev_station" . "\xe56d")
("event" . "\xe878")
("event_available" . "\xe614")
("event_busy" . "\xe615")
("event_note" . "\xe616")
("event_seat" . "\xe903")
("exit_to_app" . "\xe879")
("expand_less" . "\xe5ce")
("expand_more" . "\xe5cf")
("explicit" . "\xe01e")
("explore" . "\xe87a")
("exposure" . "\xe3ca")
("exposure_neg_1" . "\xe3cb")
("exposure_neg_2" . "\xe3cc")
("exposure_plus_1" . "\xe3cd")
("exposure_plus_2" . "\xe3ce")
("exposure_zero" . "\xe3cf")
("extension" . "\xe87b")
("face" . "\xe87c")
("fast_forward" . "\xe01f")
("fast_rewind" . "\xe020")
("favorite" . "\xe87d")
("favorite_border" . "\xe87e")
("featured_play_list" . "\xe06d")
("featured_video" . "\xe06e")
("feedback" . "\xe87f")
("fiber_dvr" . "\xe05d")
("fiber_manual_record" . "\xe061")
("fiber_new" . "\xe05e")
("fiber_pin" . "\xe06a")
("fiber_smart_record" . "\xe062")
("file_download" . "\xe2c4")
("file_upload" . "\xe2c6")
("filter" . "\xe3d3")
("filter_1" . "\xe3d0")
("filter_2" . "\xe3d1")
("filter_3" . "\xe3d2")
("filter_4" . "\xe3d4")
("filter_5" . "\xe3d5")
("filter_6" . "\xe3d6")
("filter_7" . "\xe3d7")
("filter_8" . "\xe3d8")
("filter_9" . "\xe3d9")
("filter_9_plus" . "\xe3da")
("filter_b_and_w" . "\xe3db")
("filter_center_focus" . "\xe3dc")
("filter_drama" . "\xe3dd")
("filter_frames" . "\xe3de")
("filter_hdr" . "\xe3df")
("filter_list" . "\xe152")
("filter_none" . "\xe3e0")
("filter_tilt_shift" . "\xe3e2")
("filter_vintage" . "\xe3e3")
("find_in_page" . "\xe880")
("find_replace" . "\xe881")
("fingerprint" . "\xe90d")
("first_page" . "\xe5dc")
("fitness_center" . "\xeb43")
("flag" . "\xe153")
("flare" . "\xe3e4")
("flash_auto" . "\xe3e5")
("flash_off" . "\xe3e6")
("flash_on" . "\xe3e7")
("flight" . "\xe539")
("flight_land" . "\xe904")
("flight_takeoff" . "\xe905")
("flip" . "\xe3e8")
("flip_to_back" . "\xe882")
("flip_to_front" . "\xe883")
("folder" . "\xe2c7")
("folder_open" . "\xe2c8")
("folder_shared" . "\xe2c9")
("folder_special" . "\xe617")
("font_download" . "\xe167")
("format_align_center" . "\xe234")
("format_align_justify" . "\xe235")
("format_align_left" . "\xe236")
("format_align_right" . "\xe237")
("format_bold" . "\xe238")
("format_clear" . "\xe239")
("format_color_fill" . "\xe23a")
("format_color_reset" . "\xe23b")
("format_color_text" . "\xe23c")
("format_indent_decrease" . "\xe23d")
("format_indent_increase" . "\xe23e")
("format_italic" . "\xe23f")
("format_line_spacing" . "\xe240")
("format_list_bulleted" . "\xe241")
("format_list_numbered" . "\xe242")
("format_paint" . "\xe243")
("format_quote" . "\xe244")
("format_shapes" . "\xe25e")
("format_size" . "\xe245")
("format_strikethrough" . "\xe246")
("format_textdirection_l_to_r" . "\xe247")
("format_textdirection_r_to_l" . "\xe248")
("format_underlined" . "\xe249")
("forum" . "\xe0bf")
("forward" . "\xe154")
("forward_10" . "\xe056")
("forward_30" . "\xe057")
("forward_5" . "\xe058")
("free_breakfast" . "\xeb44")
("fullscreen" . "\xe5d0")
("fullscreen_exit" . "\xe5d1")
("functions" . "\xe24a")
("g_translate" . "\xe927")
("gamepad" . "\xe30f")
("games" . "\xe021")
("gavel" . "\xe90e")
("gesture" . "\xe155")
("get_app" . "\xe884")
("gif" . "\xe908")
("golf_course" . "\xeb45")
("gps_fixed" . "\xe1b3")
("gps_not_fixed" . "\xe1b4")
("gps_off" . "\xe1b5")
("grade" . "\xe885")
("gradient" . "\xe3e9")
("grain" . "\xe3ea")
("graphic_eq" . "\xe1b8")
("grid_off" . "\xe3eb")
("grid_on" . "\xe3ec")
("group" . "\xe7ef")
("group_add" . "\xe7f0")
("group_work" . "\xe886")
("hd" . "\xe052")
("hdr_off" . "\xe3ed")
("hdr_on" . "\xe3ee")
("hdr_strong" . "\xe3f1")
("hdr_weak" . "\xe3f2")
("headset" . "\xe310")
("headset_mic" . "\xe311")
("healing" . "\xe3f3")
("hearing" . "\xe023")
("help" . "\xe887")
("help_outline" . "\xe8fd")
("high_quality" . "\xe024")
("highlight" . "\xe25f")
("highlight_off" . "\xe888")
("history" . "\xe889")
("home" . "\xe88a")
("hot_tub" . "\xeb46")
("hotel" . "\xe53a")
("hourglass_empty" . "\xe88b")
("hourglass_full" . "\xe88c")
("http" . "\xe902")
("https" . "\xe88d")
("image" . "\xe3f4")
("image_aspect_ratio" . "\xe3f5")
("import_contacts" . "\xe0e0")
("import_export" . "\xe0c3")
("important_devices" . "\xe912")
("inbox" . "\xe156")
("indeterminate_check_box" . "\xe909")
("info" . "\xe88e")
("info_outline" . "\xe88f")
("input" . "\xe890")
("insert_chart" . "\xe24b")
("insert_comment" . "\xe24c")
("insert_drive_file" . "\xe24d")
("insert_emoticon" . "\xe24e")
("insert_invitation" . "\xe24f")
("insert_link" . "\xe250")
("insert_photo" . "\xe251")
("invert_colors" . "\xe891")
("invert_colors_off" . "\xe0c4")
("iso" . "\xe3f6")
("keyboard" . "\xe312")
("keyboard_arrow_down" . "\xe313")
("keyboard_arrow_left" . "\xe314")
("keyboard_arrow_right" . "\xe315")
("keyboard_arrow_up" . "\xe316")
("keyboard_backspace" . "\xe317")
("keyboard_capslock" . "\xe318")
("keyboard_hide" . "\xe31a")
("keyboard_return" . "\xe31b")
("keyboard_tab" . "\xe31c")
("keyboard_voice" . "\xe31d")
("kitchen" . "\xeb47")
("label" . "\xe892")
("label_outline" . "\xe893")
("landscape" . "\xe3f7")
("language" . "\xe894")
("laptop" . "\xe31e")
("laptop_chromebook" . "\xe31f")
("laptop_mac" . "\xe320")
("laptop_windows" . "\xe321")
("last_page" . "\xe5dd")
("launch" . "\xe895")
("layers" . "\xe53b")
("layers_clear" . "\xe53c")
("leak_add" . "\xe3f8")
("leak_remove" . "\xe3f9")
("lens" . "\xe3fa")
("library_add" . "\xe02e")
("library_books" . "\xe02f")
("library_music" . "\xe030")
("lightbulb_outline" . "\xe90f")
("line_style" . "\xe919")
("line_weight" . "\xe91a")
("linear_scale" . "\xe260")
("link" . "\xe157")
("linked_camera" . "\xe438")
("list" . "\xe896")
("live_help" . "\xe0c6")
("live_tv" . "\xe639")
("local_activity" . "\xe53f")
("local_airport" . "\xe53d")
("local_atm" . "\xe53e")
("local_bar" . "\xe540")
("local_cafe" . "\xe541")
("local_car_wash" . "\xe542")
("local_convenience_store" . "\xe543")
("local_dining" . "\xe556")
("local_drink" . "\xe544")
("local_florist" . "\xe545")
("local_gas_station" . "\xe546")
("local_grocery_store" . "\xe547")
("local_hospital" . "\xe548")
("local_hotel" . "\xe549")
("local_laundry_service" . "\xe54a")
("local_library" . "\xe54b")
("local_mall" . "\xe54c")
("local_movies" . "\xe54d")
("local_offer" . "\xe54e")
("local_parking" . "\xe54f")
("local_pharmacy" . "\xe550")
("local_phone" . "\xe551")
("local_pizza" . "\xe552")
("local_play" . "\xe553")
("local_post_office" . "\xe554")
("local_printshop" . "\xe555")
("local_see" . "\xe557")
("local_shipping" . "\xe558")
("local_taxi" . "\xe559")
("location_city" . "\xe7f1")
("location_disabled" . "\xe1b6")
("location_off" . "\xe0c7")
("location_on" . "\xe0c8")
("location_searching" . "\xe1b7")
("lock" . "\xe897")
("lock_open" . "\xe898")
("lock_outline" . "\xe899")
("looks" . "\xe3fc")
("looks_3" . "\xe3fb")
("looks_4" . "\xe3fd")
("looks_5" . "\xe3fe")
("looks_6" . "\xe3ff")
("looks_one" . "\xe400")
("looks_two" . "\xe401")
("loop" . "\xe028")
("loupe" . "\xe402")
("low_priority" . "\xe16d")
("loyalty" . "\xe89a")
("mail" . "\xe158")
("mail_outline" . "\xe0e1")
("map" . "\xe55b")
("markunread" . "\xe159")
("markunread_mailbox" . "\xe89b")
("memory" . "\xe322")
("menu" . "\xe5d2")
("merge_type" . "\xe252")
("message" . "\xe0c9")
("mic" . "\xe029")
("mic_none" . "\xe02a")
("mic_off" . "\xe02b")
("mms" . "\xe618")
("mode_comment" . "\xe253")
("mode_edit" . "\xe254")
("monetization_on" . "\xe263")
("money_off" . "\xe25c")
("monochrome_photos" . "\xe403")
("mood" . "\xe7f2")
("mood_bad" . "\xe7f3")
("more" . "\xe619")
("more_horiz" . "\xe5d3")
("more_vert" . "\xe5d4")
("motorcycle" . "\xe91b")
("mouse" . "\xe323")
("move_to_inbox" . "\xe168")
("movie" . "\xe02c")
("movie_creation" . "\xe404")
("movie_filter" . "\xe43a")
("multiline_chart" . "\xe6df")
("music_note" . "\xe405")
("music_video" . "\xe063")
("my_location" . "\xe55c")
("nature" . "\xe406")
("nature_people" . "\xe407")
("navigate_before" . "\xe408")
("navigate_next" . "\xe409")
("navigation" . "\xe55d")
("near_me" . "\xe569")
("network_cell" . "\xe1b9")
("network_check" . "\xe640")
("network_locked" . "\xe61a")
("network_wifi" . "\xe1ba")
("new_releases" . "\xe031")
("next_week" . "\xe16a")
("nfc" . "\xe1bb")
("no_encryption" . "\xe641")
("no_sim" . "\xe0cc")
("not_interested" . "\xe033")
("note" . "\xe06f")
("note_add" . "\xe89c")
("notifications" . "\xe7f4")
("notifications_active" . "\xe7f7")
("notifications_none" . "\xe7f5")
("notifications_off" . "\xe7f6")
("notifications_paused" . "\xe7f8")
("offline_pin" . "\xe90a")
("ondemand_video" . "\xe63a")
("opacity" . "\xe91c")
("open_in_browser" . "\xe89d")
("open_in_new" . "\xe89e")
("open_with" . "\xe89f")
("pages" . "\xe7f9")
("pageview" . "\xe8a0")
("palette" . "\xe40a")
("pan_tool" . "\xe925")
("panorama" . "\xe40b")
("panorama_fish_eye" . "\xe40c")
("panorama_horizontal" . "\xe40d")
("panorama_vertical" . "\xe40e")
("panorama_wide_angle" . "\xe40f")
("party_mode" . "\xe7fa")
("pause" . "\xe034")
("pause_circle_filled" . "\xe035")
("pause_circle_outline" . "\xe036")
("payment" . "\xe8a1")
("people" . "\xe7fb")
("people_outline" . "\xe7fc")
("perm_camera_mic" . "\xe8a2")
("perm_contact_calendar" . "\xe8a3")
("perm_data_setting" . "\xe8a4")
("perm_device_information" . "\xe8a5")
("perm_identity" . "\xe8a6")
("perm_media" . "\xe8a7")
("perm_phone_msg" . "\xe8a8")
("perm_scan_wifi" . "\xe8a9")
("person" . "\xe7fd")
("person_add" . "\xe7fe")
("person_outline" . "\xe7ff")
("person_pin" . "\xe55a")
("person_pin_circle" . "\xe56a")
("personal_video" . "\xe63b")
("pets" . "\xe91d")
("phone" . "\xe0cd")
("phone_android" . "\xe324")
("phone_bluetooth_speaker" . "\xe61b")
("phone_forwarded" . "\xe61c")
("phone_in_talk" . "\xe61d")
("phone_iphone" . "\xe325")
("phone_locked" . "\xe61e")
("phone_missed" . "\xe61f")
("phone_paused" . "\xe620")
("phonelink" . "\xe326")
("phonelink_erase" . "\xe0db")
("phonelink_lock" . "\xe0dc")
("phonelink_off" . "\xe327")
("phonelink_ring" . "\xe0dd")
("phonelink_setup" . "\xe0de")
("photo" . "\xe410")
("photo_album" . "\xe411")
("photo_camera" . "\xe412")
("photo_filter" . "\xe43b")
("photo_library" . "\xe413")
("photo_size_select_actual" . "\xe432")
("photo_size_select_large" . "\xe433")
("photo_size_select_small" . "\xe434")
("picture_as_pdf" . "\xe415")
("picture_in_picture" . "\xe8aa")
("picture_in_picture_alt" . "\xe911")
("pie_chart" . "\xe6c4")
("pie_chart_outlined" . "\xe6c5")
("pin_drop" . "\xe55e")
("place" . "\xe55f")
("play_arrow" . "\xe037")
("play_circle_filled" . "\xe038")
("play_circle_outline" . "\xe039")
("play_for_work" . "\xe906")
("playlist_add" . "\xe03b")
("playlist_add_check" . "\xe065")
("playlist_play" . "\xe05f")
("plus_one" . "\xe800")
("poll" . "\xe801")
("polymer" . "\xe8ab")
("pool" . "\xeb48")
("portable_wifi_off" . "\xe0ce")
("portrait" . "\xe416")
("power" . "\xe63c")
("power_input" . "\xe336")
("power_settings_new" . "\xe8ac")
("pregnant_woman" . "\xe91e")
("present_to_all" . "\xe0df")
("print" . "\xe8ad")
("priority_high" . "\xe645")
("public" . "\xe80b")
("publish" . "\xe255")
("query_builder" . "\xe8ae")
("question_answer" . "\xe8af")
("queue" . "\xe03c")
("queue_music" . "\xe03d")
("queue_play_next" . "\xe066")
("radio" . "\xe03e")
("radio_button_checked" . "\xe837")
("radio_button_unchecked" . "\xe836")
("rate_review" . "\xe560")
("receipt" . "\xe8b0")
("recent_actors" . "\xe03f")
("record_voice_over" . "\xe91f")
("redeem" . "\xe8b1")
("redo" . "\xe15a")
("refresh" . "\xe5d5")
("remove" . "\xe15b")
("remove_circle" . "\xe15c")
("remove_circle_outline" . "\xe15d")
("remove_from_queue" . "\xe067")
("remove_red_eye" . "\xe417")
("remove_shopping_cart" . "\xe928")
("reorder" . "\xe8fe")
("repeat" . "\xe040")
("repeat_one" . "\xe041")
("replay" . "\xe042")
("replay_10" . "\xe059")
("replay_30" . "\xe05a")
("replay_5" . "\xe05b")
("reply" . "\xe15e")
("reply_all" . "\xe15f")
("report" . "\xe160")
("report_problem" . "\xe8b2")
("restaurant" . "\xe56c")
("restaurant_menu" . "\xe561")
("restore" . "\xe8b3")
("restore_page" . "\xe929")
("ring_volume" . "\xe0d1")
("room" . "\xe8b4")
("room_service" . "\xeb49")
("rotate_90_degrees_ccw" . "\xe418")
("rotate_left" . "\xe419")
("rotate_right" . "\xe41a")
("rounded_corner" . "\xe920")
("router" . "\xe328")
("rowing" . "\xe921")
("rss_feed" . "\xe0e5")
("rv_hookup" . "\xe642")
("satellite" . "\xe562")
("save" . "\xe161")
("scanner" . "\xe329")
("schedule" . "\xe8b5")
("school" . "\xe80c")
("screen_lock_landscape" . "\xe1be")
("screen_lock_portrait" . "\xe1bf")
("screen_lock_rotation" . "\xe1c0")
("screen_rotation" . "\xe1c1")
("screen_share" . "\xe0e2")
("sd_card" . "\xe623")
("sd_storage" . "\xe1c2")
("search" . "\xe8b6")
("security" . "\xe32a")
("select_all" . "\xe162")
("send" . "\xe163")
("sentiment_dissatisfied" . "\xe811")
("sentiment_neutral" . "\xe812")
("sentiment_satisfied" . "\xe813")
("sentiment_very_dissatisfied" . "\xe814")
("sentiment_very_satisfied" . "\xe815")
("settings" . "\xe8b8")
("settings_applications" . "\xe8b9")
("settings_backup_restore" . "\xe8ba")
("settings_bluetooth" . "\xe8bb")
("settings_brightness" . "\xe8bd")
("settings_cell" . "\xe8bc")
("settings_ethernet" . "\xe8be")
("settings_input_antenna" . "\xe8bf")
("settings_input_component" . "\xe8c0")
("settings_input_composite" . "\xe8c1")
("settings_input_hdmi" . "\xe8c2")
("settings_input_svideo" . "\xe8c3")
("settings_overscan" . "\xe8c4")
("settings_phone" . "\xe8c5")
("settings_power" . "\xe8c6")
("settings_remote" . "\xe8c7")
("settings_system_daydream" . "\xe1c3")
("settings_voice" . "\xe8c8")
("share" . "\xe80d")
("shop" . "\xe8c9")
("shop_two" . "\xe8ca")
("shopping_basket" . "\xe8cb")
("shopping_cart" . "\xe8cc")
("short_text" . "\xe261")
("show_chart" . "\xe6e1")
("shuffle" . "\xe043")
("signal_cellular_4_bar" . "\xe1c8")
("signal_cellular_connected_no_internet_4_bar" . "\xe1cd")
("signal_cellular_no_sim" . "\xe1ce")
("signal_cellular_null" . "\xe1cf")
("signal_cellular_off" . "\xe1d0")
("signal_wifi_4_bar" . "\xe1d8")
("signal_wifi_4_bar_lock" . "\xe1d9")
("signal_wifi_off" . "\xe1da")
("sim_card" . "\xe32b")
("sim_card_alert" . "\xe624")
("skip_next" . "\xe044")
("skip_previous" . "\xe045")
("slideshow" . "\xe41b")
("slow_motion_video" . "\xe068")
("smartphone" . "\xe32c")
("smoke_free" . "\xeb4a")
("smoking_rooms" . "\xeb4b")
("sms" . "\xe625")
("sms_failed" . "\xe626")
("snooze" . "\xe046")
("sort" . "\xe164")
("sort_by_alpha" . "\xe053")
("spa" . "\xeb4c")
("space_bar" . "\xe256")
("speaker" . "\xe32d")
("speaker_group" . "\xe32e")
("speaker_notes" . "\xe8cd")
("speaker_notes_off" . "\xe92a")
("speaker_phone" . "\xe0d2")
("spellcheck" . "\xe8ce")
("star" . "\xe838")
("star_border" . "\xe83a")
("star_half" . "\xe839")
("stars" . "\xe8d0")
("stay_current_landscape" . "\xe0d3")
("stay_current_portrait" . "\xe0d4")
("stay_primary_landscape" . "\xe0d5")
("stay_primary_portrait" . "\xe0d6")
("stop" . "\xe047")
("stop_screen_share" . "\xe0e3")
("storage" . "\xe1db")
("store" . "\xe8d1")
("store_mall_directory" . "\xe563")
("straighten" . "\xe41c")
("streetview" . "\xe56e")
("strikethrough_s" . "\xe257")
("style" . "\xe41d")
("subdirectory_arrow_left" . "\xe5d9")
("subdirectory_arrow_right" . "\xe5da")
("subject" . "\xe8d2")
("subscriptions" . "\xe064")
("subtitles" . "\xe048")
("subway" . "\xe56f")
("supervisor_account" . "\xe8d3")
("surround_sound" . "\xe049")
("swap_calls" . "\xe0d7")
("swap_horiz" . "\xe8d4")
("swap_vert" . "\xe8d5")
("swap_vertical_circle" . "\xe8d6")
("switch_camera" . "\xe41e")
("switch_video" . "\xe41f")
("sync" . "\xe627")
("sync_disabled" . "\xe628")
("sync_problem" . "\xe629")
("system_update" . "\xe62a")
("system_update_alt" . "\xe8d7")
("tab" . "\xe8d8")
("tab_unselected" . "\xe8d9")
("tablet" . "\xe32f")
("tablet_android" . "\xe330")
("tablet_mac" . "\xe331")
("tag_faces" . "\xe420")
("tap_and_play" . "\xe62b")
("terrain" . "\xe564")
("text_fields" . "\xe262")
("text_format" . "\xe165")
("textsms" . "\xe0d8")
("texture" . "\xe421")
("theaters" . "\xe8da")
("thumb_down" . "\xe8db")
("thumb_up" . "\xe8dc")
("thumbs_up_down" . "\xe8dd")
("time_to_leave" . "\xe62c")
("timelapse" . "\xe422")
("timeline" . "\xe922")
("timer" . "\xe425")
("timer_10" . "\xe423")
("timer_3" . "\xe424")
("timer_off" . "\xe426")
("title" . "\xe264")
("toc" . "\xe8de")
("today" . "\xe8df")
("toll" . "\xe8e0")
("tonality" . "\xe427")
("touch_app" . "\xe913")
("toys" . "\xe332")
("track_changes" . "\xe8e1")
("traffic" . "\xe565")
("train" . "\xe570")
("tram" . "\xe571")
("transfer_within_a_station" . "\xe572")
("transform" . "\xe428")
("translate" . "\xe8e2")
("trending_down" . "\xe8e3")
("trending_flat" . "\xe8e4")
("trending_up" . "\xe8e5")
("tune" . "\xe429")
("turned_in" . "\xe8e6")
("turned_in_not" . "\xe8e7")
("tv" . "\xe333")
("unarchive" . "\xe169")
("undo" . "\xe166")
("unfold_less" . "\xe5d6")
("unfold_more" . "\xe5d7")
("update" . "\xe923")
("usb" . "\xe1e0")
("verified_user" . "\xe8e8")
("vertical_align_bottom" . "\xe258")
("vertical_align_center" . "\xe259")
("vertical_align_top" . "\xe25a")
("vibration" . "\xe62d")
("video_call" . "\xe070")
("video_label" . "\xe071")
("video_library" . "\xe04a")
("videocam" . "\xe04b")
("videocam_off" . "\xe04c")
("videogame_asset" . "\xe338")
("view_agenda" . "\xe8e9")
("view_array" . "\xe8ea")
("view_carousel" . "\xe8eb")
("view_column" . "\xe8ec")
("view_comfy" . "\xe42a")
("view_compact" . "\xe42b")
("view_day" . "\xe8ed")
("view_headline" . "\xe8ee")
("view_list" . "\xe8ef")
("view_module" . "\xe8f0")
("view_quilt" . "\xe8f1")
("view_stream" . "\xe8f2")
("view_week" . "\xe8f3")
("vignette" . "\xe435")
("visibility" . "\xe8f4")
("visibility_off" . "\xe8f5")
("voice_chat" . "\xe62e")
("voicemail" . "\xe0d9")
("volume_down" . "\xe04d")
("volume_mute" . "\xe04e")
("volume_off" . "\xe04f")
("volume_up" . "\xe050")
("vpn_key" . "\xe0da")
("vpn_lock" . "\xe62f")
("wallpaper" . "\xe1bc")
("warning" . "\xe002")
("watch" . "\xe334")
("watch_later" . "\xe924")
("wb_auto" . "\xe42c")
("wb_cloudy" . "\xe42d")
("wb_incandescent" . "\xe42e")
("wb_iridescent" . "\xe436")
("wb_sunny" . "\xe430")
("wc" . "\xe63d")
("web" . "\xe051")
("web_asset" . "\xe069")
("weekend" . "\xe16b")
("whatshot" . "\xe80e")
("widgets" . "\xe1bd")
("wifi" . "\xe63e")
("wifi_lock" . "\xe1e1")
("wifi_tethering" . "\xe1e2")
("work" . "\xe8f9")
("wrap_text" . "\xe25b")
("youtube_searched_for" . "\xe8fa")
("zoom_in" . "\xe8ff")
("zoom_out" . "\xe900")
("zoom_out_map" . "\xe56b")))
(provide 'data-material)

View file

@ -0,0 +1,165 @@
(defvar all-the-icons-data/octicons-alist
'(
("alert" . "\xf02d")
("arrow-down" . "\xf03f")
("arrow-left" . "\xf040")
("arrow-right" . "\xf03e")
("arrow-small-down" . "\xf0a0")
("arrow-small-left" . "\xf0a1")
("arrow-small-right" . "\xf071")
("arrow-small-up" . "\xf09f")
("arrow-up" . "\xf03d")
("book" . "\xf007")
("bookmark" . "\xf07b")
("briefcase" . "\xf0d3")
("broadcast" . "\xf048")
("browser" . "\xf0c5")
("bug" . "\xf091")
("calendar" . "\xf068")
("check" . "\xf03a")
("checklist" . "\xf076")
("chevron-down" . "\xf0a3")
("chevron-left" . "\xf0a4")
("chevron-right" . "\xf078")
("chevron-up" . "\xf0a2")
("circle-slash" . "\xf084")
("circuit-board" . "\xf0d6")
("clippy" . "\xf035")
("clock" . "\xf046")
("cloud-download" . "\xf00b")
("cloud-upload" . "\xf00c")
("code" . "\xf05f")
("comment" . "\xf02b")
("comment-discussion" . "\xf04f")
("credit-card" . "\xf045")
("dash" . "\xf0ca")
("dashboard" . "\xf07d")
("database" . "\xf096")
("device-camera" . "\xf056")
("device-camera-video" . "\xf057")
("device-desktop" . "\xf27c")
("device-mobile" . "\xf038")
("diff" . "\xf04d")
("diff-added" . "\xf06b")
("diff-ignored" . "\xf099")
("diff-modified" . "\xf06d")
("diff-removed" . "\xf06c")
("diff-renamed" . "\xf06e")
("ellipsis" . "\xf09a")
("eye" . "\xf04e")
("file-binary" . "\xf094")
("file-code" . "\xf010")
("file-directory" . "\xf016")
("file-media" . "\xf012")
("file-pdf" . "\xf014")
("file-submodule" . "\xf017")
("file-symlink-directory" . "\xf0b1")
("file-symlink-file" . "\xf0b0")
("file-text" . "\xf011")
("file-zip" . "\xf013")
("flame" . "\xf0d2")
("fold" . "\xf0cc")
("gear" . "\xf02f")
("gift" . "\xf042")
("gist" . "\xf00e")
("gist-secret" . "\xf08c")
("git-branch" . "\xf020")
("git-commit" . "\xf01f")
("git-compare" . "\xf0ac")
("git-merge" . "\xf023")
("git-pull-request" . "\xf009")
("globe" . "\xf0b6")
("graph" . "\xf043")
("beaker" . "\xf0dd")
("heart" . "\x2665")
("history" . "\xf07e")
("home" . "\xf08d")
("horizontal-rule" . "\xf070")
("hourglass" . "\xf09e")
("hubot" . "\xf09d")
("inbox" . "\xf0cf")
("info" . "\xf059")
("issue-closed" . "\xf028")
("issue-opened" . "\xf026")
("issue-reopened" . "\xf027")
("jersey" . "\xf019")
("key" . "\xf049")
("keyboard" . "\xf00d")
("law" . "\xf0d8")
("light-bulb" . "\xf000")
("link" . "\xf05c")
("link-external" . "\xf07f")
("list-ordered" . "\xf062")
("list-unordered" . "\xf061")
("location" . "\xf060")
("lock" . "\xf06a")
("logo-github" . "\xf092")
("mail" . "\xf03b")
("mail-read" . "\xf03c")
("mail-reply" . "\xf051")
("mark-github" . "\xf00a")
("markdown" . "\xf0c9")
("megaphone" . "\xf077")
("mention" . "\xf0be")
("milestone" . "\xf075")
("mirror" . "\xf024")
("mortar-board" . "\xf0d7")
("mute" . "\xf080")
("no-newline" . "\xf09c")
("octoface" . "\xf008")
("organization" . "\xf037")
("package" . "\xf0c4")
("paintcan" . "\xf0d1")
("pencil" . "\xf058")
("person" . "\xf018")
("pin" . "\xf041")
("plug" . "\xf0d4")
("plus" . "\xf05d")
("primitive-dot" . "\xf052")
("primitive-square" . "\xf053")
("pulse" . "\xf085")
("puzzle" . "\xf0c0")
("question" . "\xf02c")
("quote" . "\xf063")
("radio-tower" . "\xf030")
("repo" . "\xf001")
("repo-clone" . "\xf04c")
("repo-force-push" . "\xf04a")
("repo-forked" . "\xf002")
("repo-pull" . "\xf006")
("repo-push" . "\xf005")
("rocket" . "\xf033")
("rss" . "\xf034")
("ruby" . "\xf047")
("search" . "\xf02e")
("server" . "\xf097")
("settings" . "\xf07c")
("sign-in" . "\xf036")
("sign-out" . "\xf032")
("squirrel" . "\xf0b2")
("star" . "\xf02a")
("steps" . "\xf0c7")
("stop" . "\xf08f")
("sync" . "\xf087")
("tag" . "\xf015")
("telescope" . "\xf088")
("terminal" . "\xf0c8")
("three-bars" . "\xf05e")
("thumbsdown" . "\xf0db")
("thumbsup" . "\xf0da")
("tools" . "\xf031")
("trashcan" . "\xf0d0")
("triangle-down" . "\xf05b")
("triangle-left" . "\xf044")
("triangle-right" . "\xf05a")
("triangle-up" . "\xf0aa")
("unfold" . "\xf039")
("unmute" . "\xf0ba")
("versions" . "\xf064")
("x" . "\xf081")
("zap" . "\x26A1")
))
(provide 'data-octicons)

View file

@ -0,0 +1,594 @@
(defvar all-the-icons-data/weather-icons-alist
'(
("alien" . "\xf075")
("barometer" . "\xf079")
("celsius" . "\xf03c")
("cloud" . "\xf041")
("cloud-down" . "\xf03d")
("cloud-refresh" . "\xf03e")
("cloud-up" . "\xf040")
("cloudy" . "\xf013")
("cloudy-gusts" . "\xf011")
("cloudy-windy" . "\xf012")
("day-cloudy" . "\xf002")
("day-cloudy-gusts" . "\xf000")
("day-cloudy-high" . "\xf07d")
("day-cloudy-windy" . "\xf001")
("day-fog" . "\xf003")
("day-hail" . "\xf004")
("day-haze" . "\xf0b6")
("day-light-wind" . "\xf0c4")
("day-lightning" . "\xf005")
("day-rain" . "\xf008")
("day-rain-mix" . "\xf006")
("day-rain-wind" . "\xf007")
("day-showers" . "\xf009")
("day-sleet" . "\xf0b2")
("day-sleet-storm" . "\xf068")
("day-snow" . "\xf00a")
("day-snow-thunderstorm" . "\xf06b")
("day-snow-wind" . "\xf065")
("day-sprinkle" . "\xf00b")
("day-storm-showers" . "\xf00e")
("day-sunny" . "\xf00d")
("day-sunny-overcast" . "\xf00c")
("day-thunderstorm" . "\xf010")
("day-windy" . "\xf085")
("degrees" . "\xf042")
("direction-down" . "\xf044")
("direction-down-left" . "\xf043")
("direction-down-right" . "\xf088")
("direction-left" . "\xf048")
("direction-right" . "\xf04d")
("direction-up" . "\xf058")
("direction-up-left" . "\xf087")
("direction-up-right" . "\xf057")
("dust" . "\xf063")
("earthquake" . "\xf0c6")
("fahrenheit" . "\xf045")
("fire" . "\xf0c7")
("flood" . "\xf07c")
("fog" . "\xf014")
("forecast-io-clear-day" . "\xf00d")
("forecast-io-clear-night" . "\xf02e")
("forecast-io-cloudy" . "\xf013")
("forecast-io-fog" . "\xf014")
("forecast-io-hail" . "\xf015")
("forecast-io-partly-cloudy-day" . "\xf002")
("forecast-io-partly-cloudy-night" . "\xf031")
("forecast-io-rain" . "\xf019")
("forecast-io-sleet" . "\xf0b5")
("forecast-io-snow" . "\xf01b")
("forecast-io-thunderstorm" . "\xf01e")
("forecast-io-tornado" . "\xf056")
("forecast-io-wind" . "\xf050")
("gale-warning" . "\xf0cd")
("hail" . "\xf015")
("horizon" . "\xf047")
("horizon-alt" . "\xf046")
("hot" . "\xf072")
("humidity" . "\xf07a")
("hurricane" . "\xf073")
("hurricane-warning" . "\xf0cf")
("lightning" . "\xf016")
("lunar-eclipse" . "\xf070")
("meteor" . "\xf071")
("moon-0" . "\xf095")
("moon-1" . "\xf096")
("moon-10" . "\xf09f")
("moon-11" . "\xf0a0")
("moon-12" . "\xf0a1")
("moon-13" . "\xf0a2")
("moon-14" . "\xf0a3")
("moon-15" . "\xf0a4")
("moon-16" . "\xf0a5")
("moon-17" . "\xf0a6")
("moon-18" . "\xf0a7")
("moon-19" . "\xf0a8")
("moon-2" . "\xf097")
("moon-20" . "\xf0a9")
("moon-21" . "\xf0aa")
("moon-22" . "\xf0ab")
("moon-23" . "\xf0ac")
("moon-24" . "\xf0ad")
("moon-25" . "\xf0ae")
("moon-26" . "\xf0af")
("moon-27" . "\xf0b0")
("moon-3" . "\xf098")
("moon-4" . "\xf099")
("moon-5" . "\xf09a")
("moon-6" . "\xf09b")
("moon-7" . "\xf09c")
("moon-8" . "\xf09d")
("moon-9" . "\xf09e")
("moon-alt-first-quarter" . "\xf0d6")
("moon-alt-full" . "\xf0dd")
("moon-alt-new" . "\xf0eb")
("moon-alt-third-quarter" . "\xf0e4")
("moon-alt-waning-crescent-1" . "\xf0e5")
("moon-alt-waning-crescent-2" . "\xf0e6")
("moon-alt-waning-crescent-3" . "\xf0e7")
("moon-alt-waning-crescent-4" . "\xf0e8")
("moon-alt-waning-crescent-5" . "\xf0e9")
("moon-alt-waning-crescent-6" . "\xf0ea")
("moon-alt-waning-gibbous-1" . "\xf0de")
("moon-alt-waning-gibbous-2" . "\xf0df")
("moon-alt-waning-gibbous-3" . "\xf0e0")
("moon-alt-waning-gibbous-4" . "\xf0e1")
("moon-alt-waning-gibbous-5" . "\xf0e2")
("moon-alt-waning-gibbous-6" . "\xf0e3")
("moon-alt-waxing-crescent-1" . "\xf0d0")
("moon-alt-waxing-crescent-2" . "\xf0d1")
("moon-alt-waxing-crescent-3" . "\xf0d2")
("moon-alt-waxing-crescent-4" . "\xf0d3")
("moon-alt-waxing-crescent-5" . "\xf0d4")
("moon-alt-waxing-crescent-6" . "\xf0d5")
("moon-alt-waxing-gibbous-1" . "\xf0d7")
("moon-alt-waxing-gibbous-2" . "\xf0d8")
("moon-alt-waxing-gibbous-3" . "\xf0d9")
("moon-alt-waxing-gibbous-4" . "\xf0da")
("moon-alt-waxing-gibbous-5" . "\xf0db")
("moon-alt-waxing-gibbous-6" . "\xf0dc")
("moon-first-quarter" . "\xf09c")
("moon-full" . "\xf0a3")
("moon-new" . "\xf095")
("moon-third-quarter" . "\xf0aa")
("moon-waning-crescent-1" . "\xf0ab")
("moon-waning-crescent-2" . "\xf0ac")
("moon-waning-crescent-3" . "\xf0ad")
("moon-waning-crescent-4" . "\xf0ae")
("moon-waning-crescent-5" . "\xf0af")
("moon-waning-crescent-6" . "\xf0b0")
("moon-waning-gibbous-1" . "\xf0a4")
("moon-waning-gibbous-2" . "\xf0a5")
("moon-waning-gibbous-3" . "\xf0a6")
("moon-waning-gibbous-4" . "\xf0a7")
("moon-waning-gibbous-5" . "\xf0a8")
("moon-waning-gibbous-6" . "\xf0a9")
("moon-waxing-crescent-1" . "\xf096")
("moon-waxing-crescent-2" . "\xf097")
("moon-waxing-crescent-3" . "\xf098")
("moon-waxing-crescent-4" . "\xf099")
("moon-waxing-crescent-5" . "\xf09a")
("moon-waxing-crescent-6" . "\xf09b")
("moon-waxing-gibbous-1" . "\xf09d")
("moon-waxing-gibbous-2" . "\xf09e")
("moon-waxing-gibbous-3" . "\xf09f")
("moon-waxing-gibbous-4" . "\xf0a0")
("moon-waxing-gibbous-5" . "\xf0a1")
("moon-waxing-gibbous-6" . "\xf0a2")
("moonrise" . "\xf0c9")
("moonset" . "\xf0ca")
("na" . "\xf07b")
("night-alt-cloudy" . "\xf086")
("night-alt-cloudy-gusts" . "\xf022")
("night-alt-cloudy-high" . "\xf07e")
("night-alt-cloudy-windy" . "\xf023")
("night-alt-hail" . "\xf024")
("night-alt-lightning" . "\xf025")
("night-alt-partly-cloudy" . "\xf081")
("night-alt-rain" . "\xf028")
("night-alt-rain-mix" . "\xf026")
("night-alt-rain-wind" . "\xf027")
("night-alt-showers" . "\xf029")
("night-alt-sleet" . "\xf0b4")
("night-alt-sleet-storm" . "\xf06a")
("night-alt-snow" . "\xf02a")
("night-alt-snow-thunderstorm" . "\xf06d")
("night-alt-snow-wind" . "\xf067")
("night-alt-sprinkle" . "\xf02b")
("night-alt-storm-showers" . "\xf02c")
("night-alt-thunderstorm" . "\xf02d")
("night-clear" . "\xf02e")
("night-cloudy" . "\xf031")
("night-cloudy-gusts" . "\xf02f")
("night-cloudy-high" . "\xf080")
("night-cloudy-windy" . "\xf030")
("night-fog" . "\xf04a")
("night-hail" . "\xf032")
("night-lightning" . "\xf033")
("night-partly-cloudy" . "\xf083")
("night-rain" . "\xf036")
("night-rain-mix" . "\xf034")
("night-rain-wind" . "\xf035")
("night-showers" . "\xf037")
("night-sleet" . "\xf0b3")
("night-sleet-storm" . "\xf069")
("night-snow" . "\xf038")
("night-snow-thunderstorm" . "\xf06c")
("night-snow-wind" . "\xf066")
("night-sprinkle" . "\xf039")
("night-storm-showers" . "\xf03a")
("night-thunderstorm" . "\xf03b")
("owm-200" . "\xf01e")
("owm-201" . "\xf01e")
("owm-202" . "\xf01e")
("owm-210" . "\xf016")
("owm-211" . "\xf016")
("owm-212" . "\xf016")
("owm-221" . "\xf016")
("owm-230" . "\xf01e")
("owm-231" . "\xf01e")
("owm-232" . "\xf01e")
("owm-300" . "\xf01c")
("owm-301" . "\xf01c")
("owm-302" . "\xf019")
("owm-310" . "\xf017")
("owm-311" . "\xf019")
("owm-312" . "\xf019")
("owm-313" . "\xf01a")
("owm-314" . "\xf019")
("owm-321" . "\xf01c")
("owm-500" . "\xf01c")
("owm-501" . "\xf019")
("owm-502" . "\xf019")
("owm-503" . "\xf019")
("owm-504" . "\xf019")
("owm-511" . "\xf017")
("owm-520" . "\xf01a")
("owm-521" . "\xf01a")
("owm-522" . "\xf01a")
("owm-531" . "\xf01d")
("owm-600" . "\xf01b")
("owm-601" . "\xf01b")
("owm-602" . "\xf0b5")
("owm-611" . "\xf017")
("owm-612" . "\xf017")
("owm-615" . "\xf017")
("owm-616" . "\xf017")
("owm-620" . "\xf017")
("owm-621" . "\xf01b")
("owm-622" . "\xf01b")
("owm-701" . "\xf01a")
("owm-711" . "\xf062")
("owm-721" . "\xf0b6")
("owm-731" . "\xf063")
("owm-741" . "\xf014")
("owm-761" . "\xf063")
("owm-762" . "\xf063")
("owm-771" . "\xf011")
("owm-781" . "\xf056")
("owm-800" . "\xf00d")
("owm-801" . "\xf011")
("owm-802" . "\xf011")
("owm-803" . "\xf012")
("owm-804" . "\xf013")
("owm-900" . "\xf056")
("owm-901" . "\xf01d")
("owm-902" . "\xf073")
("owm-903" . "\xf076")
("owm-904" . "\xf072")
("owm-905" . "\xf021")
("owm-906" . "\xf015")
("owm-957" . "\xf050")
("owm-day-200" . "\xf010")
("owm-day-201" . "\xf010")
("owm-day-202" . "\xf010")
("owm-day-210" . "\xf005")
("owm-day-211" . "\xf005")
("owm-day-212" . "\xf005")
("owm-day-221" . "\xf005")
("owm-day-230" . "\xf010")
("owm-day-231" . "\xf010")
("owm-day-232" . "\xf010")
("owm-day-300" . "\xf00b")
("owm-day-301" . "\xf00b")
("owm-day-302" . "\xf008")
("owm-day-310" . "\xf008")
("owm-day-311" . "\xf008")
("owm-day-312" . "\xf008")
("owm-day-313" . "\xf008")
("owm-day-314" . "\xf008")
("owm-day-321" . "\xf00b")
("owm-day-500" . "\xf00b")
("owm-day-501" . "\xf008")
("owm-day-502" . "\xf008")
("owm-day-503" . "\xf008")
("owm-day-504" . "\xf008")
("owm-day-511" . "\xf006")
("owm-day-520" . "\xf009")
("owm-day-521" . "\xf009")
("owm-day-522" . "\xf009")
("owm-day-531" . "\xf00e")
("owm-day-600" . "\xf00a")
("owm-day-601" . "\xf0b2")
("owm-day-602" . "\xf00a")
("owm-day-611" . "\xf006")
("owm-day-612" . "\xf006")
("owm-day-615" . "\xf006")
("owm-day-616" . "\xf006")
("owm-day-620" . "\xf006")
("owm-day-621" . "\xf00a")
("owm-day-622" . "\xf00a")
("owm-day-701" . "\xf009")
("owm-day-711" . "\xf062")
("owm-day-721" . "\xf0b6")
("owm-day-731" . "\xf063")
("owm-day-741" . "\xf003")
("owm-day-761" . "\xf063")
("owm-day-762" . "\xf063")
("owm-day-781" . "\xf056")
("owm-day-800" . "\xf00d")
("owm-day-801" . "\xf000")
("owm-day-802" . "\xf000")
("owm-day-803" . "\xf000")
("owm-day-804" . "\xf00c")
("owm-day-900" . "\xf056")
("owm-day-902" . "\xf073")
("owm-day-903" . "\xf076")
("owm-day-904" . "\xf072")
("owm-day-906" . "\xf004")
("owm-day-957" . "\xf050")
("owm-night-200" . "\xf02d")
("owm-night-201" . "\xf02d")
("owm-night-202" . "\xf02d")
("owm-night-210" . "\xf025")
("owm-night-211" . "\xf025")
("owm-night-212" . "\xf025")
("owm-night-221" . "\xf025")
("owm-night-230" . "\xf02d")
("owm-night-231" . "\xf02d")
("owm-night-232" . "\xf02d")
("owm-night-300" . "\xf02b")
("owm-night-301" . "\xf02b")
("owm-night-302" . "\xf028")
("owm-night-310" . "\xf028")
("owm-night-311" . "\xf028")
("owm-night-312" . "\xf028")
("owm-night-313" . "\xf028")
("owm-night-314" . "\xf028")
("owm-night-321" . "\xf02b")
("owm-night-500" . "\xf02b")
("owm-night-501" . "\xf028")
("owm-night-502" . "\xf028")
("owm-night-503" . "\xf028")
("owm-night-504" . "\xf028")
("owm-night-511" . "\xf026")
("owm-night-520" . "\xf029")
("owm-night-521" . "\xf029")
("owm-night-522" . "\xf029")
("owm-night-531" . "\xf02c")
("owm-night-600" . "\xf02a")
("owm-night-601" . "\xf0b4")
("owm-night-602" . "\xf02a")
("owm-night-611" . "\xf026")
("owm-night-612" . "\xf026")
("owm-night-615" . "\xf026")
("owm-night-616" . "\xf026")
("owm-night-620" . "\xf026")
("owm-night-621" . "\xf02a")
("owm-night-622" . "\xf02a")
("owm-night-701" . "\xf029")
("owm-night-711" . "\xf062")
("owm-night-721" . "\xf0b6")
("owm-night-731" . "\xf063")
("owm-night-741" . "\xf04a")
("owm-night-761" . "\xf063")
("owm-night-762" . "\xf063")
("owm-night-781" . "\xf056")
("owm-night-800" . "\xf02e")
("owm-night-801" . "\xf022")
("owm-night-802" . "\xf022")
("owm-night-803" . "\xf022")
("owm-night-804" . "\xf086")
("owm-night-900" . "\xf056")
("owm-night-902" . "\xf073")
("owm-night-903" . "\xf076")
("owm-night-904" . "\xf072")
("owm-night-906" . "\xf024")
("owm-night-957" . "\xf050")
("rain" . "\xf019")
("rain-mix" . "\xf017")
("rain-wind" . "\xf018")
("raindrop" . "\xf078")
("raindrops" . "\xf04e")
("refresh" . "\xf04c")
("refresh-alt" . "\xf04b")
("sandstorm" . "\xf082")
("showers" . "\xf01a")
("sleet" . "\xf0b5")
("small-craft-advisory" . "\xf0cc")
("smog" . "\xf074")
("smoke" . "\xf062")
("snow" . "\xf01b")
("snow" . "\xf01b")
("snow-wind" . "\xf064")
("snowflake-cold" . "\xf076")
("solar-eclipse" . "\xf06e")
("sprinkle" . "\xf01c")
("stars" . "\xf077")
("storm-showers" . "\xf01d")
("storm-showers" . "\xf01d")
("storm-warning" . "\xf0ce")
("strong-wind" . "\xf050")
("sunrise" . "\xf051")
("sunset" . "\xf052")
("thermometer" . "\xf055")
("thermometer-exterior" . "\xf053")
("thermometer-internal" . "\xf054")
("thunderstorm" . "\xf01e")
("thunderstorm" . "\xf01e")
("time-1" . "\xf08a")
("time-10" . "\xf093")
("time-11" . "\xf094")
("time-12" . "\xf089")
("time-2" . "\xf08b")
("time-3" . "\xf08c")
("time-4" . "\xf08d")
("time-5" . "\xf08e")
("time-6" . "\xf08f")
("time-7" . "\xf090")
("time-8" . "\xf091")
("time-9" . "\xf092")
("tornado" . "\xf056")
("train" . "\xf0cb")
("tsunami" . "\xf0c5")
("umbrella" . "\xf084")
("volcano" . "\xf0c8")
("wind-beaufort-0" . "\xf0b7")
("wind-beaufort-1" . "\xf0b8")
("wind-beaufort-10" . "\xf0c1")
("wind-beaufort-11" . "\xf0c2")
("wind-beaufort-12" . "\xf0c3")
("wind-beaufort-2" . "\xf0b9")
("wind-beaufort-3" . "\xf0ba")
("wind-beaufort-4" . "\xf0bb")
("wind-beaufort-5" . "\xf0bc")
("wind-beaufort-6" . "\xf0bd")
("wind-beaufort-7" . "\xf0be")
("wind-beaufort-8" . "\xf0bf")
("wind-beaufort-9" . "\xf0c0")
("wind-direction" . "\xf0b1")
("windy" . "\xf021")
("wmo4680-00" . "\xf055")
("wmo4680-01" . "\xf013")
("wmo4680-02" . "\xf055")
("wmo4680-03" . "\xf013")
("wmo4680-04" . "\xf014")
("wmo4680-05" . "\xf014")
("wmo4680-10" . "\xf014")
("wmo4680-11" . "\xf014")
("wmo4680-12" . "\xf016")
("wmo4680-18" . "\xf050")
("wmo4680-20" . "\xf014")
("wmo4680-21" . "\xf017")
("wmo4680-22" . "\xf017")
("wmo4680-23" . "\xf019")
("wmo4680-24" . "\xf01b")
("wmo4680-25" . "\xf015")
("wmo4680-26" . "\xf01e")
("wmo4680-27" . "\xf063")
("wmo4680-28" . "\xf063")
("wmo4680-29" . "\xf063")
("wmo4680-30" . "\xf014")
("wmo4680-31" . "\xf014")
("wmo4680-32" . "\xf014")
("wmo4680-33" . "\xf014")
("wmo4680-34" . "\xf014")
("wmo4680-35" . "\xf014")
("wmo4680-40" . "\xf017")
("wmo4680-41" . "\xf01c")
("wmo4680-42" . "\xf019")
("wmo4680-43" . "\xf01c")
("wmo4680-44" . "\xf019")
("wmo4680-45" . "\xf015")
("wmo4680-46" . "\xf015")
("wmo4680-47" . "\xf01b")
("wmo4680-48" . "\xf01b")
("wmo4680-50" . "\xf01c")
("wmo4680-51" . "\xf01c")
("wmo4680-52" . "\xf019")
("wmo4680-53" . "\xf019")
("wmo4680-54" . "\xf076")
("wmo4680-55" . "\xf076")
("wmo4680-56" . "\xf076")
("wmo4680-57" . "\xf01c")
("wmo4680-58" . "\xf019")
("wmo4680-60" . "\xf01c")
("wmo4680-61" . "\xf01c")
("wmo4680-62" . "\xf019")
("wmo4680-63" . "\xf019")
("wmo4680-64" . "\xf015")
("wmo4680-65" . "\xf015")
("wmo4680-66" . "\xf015")
("wmo4680-67" . "\xf017")
("wmo4680-68" . "\xf017")
("wmo4680-70" . "\xf01b")
("wmo4680-71" . "\xf01b")
("wmo4680-72" . "\xf01b")
("wmo4680-73" . "\xf01b")
("wmo4680-74" . "\xf076")
("wmo4680-75" . "\xf076")
("wmo4680-76" . "\xf076")
("wmo4680-77" . "\xf01b")
("wmo4680-78" . "\xf076")
("wmo4680-80" . "\xf019")
("wmo4680-81" . "\xf01c")
("wmo4680-82" . "\xf019")
("wmo4680-83" . "\xf019")
("wmo4680-84" . "\xf01d")
("wmo4680-85" . "\xf017")
("wmo4680-86" . "\xf017")
("wmo4680-87" . "\xf017")
("wmo4680-89" . "\xf015")
("wmo4680-90" . "\xf016")
("wmo4680-91" . "\xf01d")
("wmo4680-92" . "\xf01e")
("wmo4680-93" . "\xf01e")
("wmo4680-94" . "\xf016")
("wmo4680-95" . "\xf01e")
("wmo4680-96" . "\xf01e")
("wmo4680-99" . "\xf056")
("wu-chanceflurries" . "\xf064")
("wu-chancerain" . "\xf019")
("wu-chancesleat" . "\xf0b5")
("wu-chancesnow" . "\xf01b")
("wu-chancetstorms" . "\xf01e")
("wu-clear" . "\xf00d")
("wu-cloudy" . "\xf002")
("wu-flurries" . "\xf064")
("wu-hazy" . "\xf0b6")
("wu-mostlycloudy" . "\xf002")
("wu-mostlysunny" . "\xf00d")
("wu-partlycloudy" . "\xf002")
("wu-partlysunny" . "\xf00d")
("wu-rain" . "\xf01a")
("wu-sleat" . "\xf0b5")
("wu-snow" . "\xf01b")
("wu-sunny" . "\xf00d")
("wu-tstorms" . "\xf01e")
("wu-unknown" . "\xf00d")
("yahoo-0" . "\xf056")
("yahoo-1" . "\xf00e")
("yahoo-10" . "\xf015")
("yahoo-11" . "\xf01a")
("yahoo-12" . "\xf01a")
("yahoo-13" . "\xf01b")
("yahoo-14" . "\xf00a")
("yahoo-15" . "\xf064")
("yahoo-16" . "\xf01b")
("yahoo-17" . "\xf015")
("yahoo-18" . "\xf017")
("yahoo-19" . "\xf063")
("yahoo-2" . "\xf073")
("yahoo-20" . "\xf014")
("yahoo-21" . "\xf021")
("yahoo-22" . "\xf062")
("yahoo-23" . "\xf050")
("yahoo-24" . "\xf050")
("yahoo-25" . "\xf076")
("yahoo-26" . "\xf013")
("yahoo-27" . "\xf031")
("yahoo-28" . "\xf002")
("yahoo-29" . "\xf031")
("yahoo-3" . "\xf01e")
("yahoo-30" . "\xf002")
("yahoo-31" . "\xf02e")
("yahoo-32" . "\xf00d")
("yahoo-3200" . "\xf077")
("yahoo-33" . "\xf083")
("yahoo-34" . "\xf00c")
("yahoo-35" . "\xf017")
("yahoo-36" . "\xf072")
("yahoo-37" . "\xf00e")
("yahoo-38" . "\xf00e")
("yahoo-39" . "\xf00e")
("yahoo-4" . "\xf01e")
("yahoo-40" . "\xf01a")
("yahoo-41" . "\xf064")
("yahoo-42" . "\xf01b")
("yahoo-43" . "\xf064")
("yahoo-44" . "\xf00c")
("yahoo-45" . "\xf00e")
("yahoo-46" . "\xf01b")
("yahoo-47" . "\xf00e")
("yahoo-5" . "\xf017")
("yahoo-6" . "\xf017")
("yahoo-7" . "\xf017")
("yahoo-8" . "\xf015")
("yahoo-9" . "\xf01a")
))
(provide 'data-weathericons)

View file

@ -0,0 +1,47 @@
;;; all-the-icons-dired-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from all-the-icons-dired.el
(autoload 'all-the-icons-dired-mode "all-the-icons-dired" "\
Display all-the-icons icon for each file in a Dired buffer.
This is a minor mode. If called interactively, toggle the
`All-The-Icons-Dired mode' mode. If the prefix argument is
positive, enable the mode, and if it is zero or negative, disable
the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `all-the-icons-dired-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(register-definition-prefixes "all-the-icons-dired" '("all-the-icons-dired-"))
;;; End of scraped data
(provide 'all-the-icons-dired-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; all-the-icons-dired-autoloads.el ends here

View file

@ -0,0 +1,15 @@
(define-package "all-the-icons-dired" "20231207.1324" "Shows icons for each file in dired mode"
'((emacs "26.1")
(all-the-icons "2.2.0"))
:commit "e157f0668f22ed586aebe0a2c0186ab07702986c" :authors
'(("jtbm37"))
:maintainers
'(("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com"))
:maintainer
'("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")
:keywords
'("files" "icons" "dired")
:url "https://github.com/wyuenho/all-the-icons-dired")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,142 @@
;;; all-the-icons-dired.el --- Shows icons for each file in dired mode -*- lexical-binding: t; -*-
;; Copyright (C) 2016-2020 jtbm37
;; Copyright (C) 2021 Jimmy Yuen Ho Wong
;; Author: jtbm37
;; Maintainer: Jimmy Yuen Ho Wong <wyuenho@gmail.com>
;; Version: 2.0
;; Keywords: files icons dired
;; Package-Requires: ((emacs "26.1") (all-the-icons "2.2.0"))
;; URL: https://github.com/wyuenho/all-the-icons-dired
;; 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; To use this package, simply add this to your init.el:
;; (add-hook 'dired-mode-hook 'all-the-icons-dired-mode)
;; To manually install, add this to your init.el before the hook mentioned above.
;; (add-to-load-path (expand-file-name "~/path/to/all-the-icons-dired"))
;; (load "all-the-icons-dired.el")
;;; Code:
(require 'dired)
(require 'all-the-icons)
(require 'subr-x)
(require 'image)
(require 'jit-lock)
(require 'font-core)
(require 'font-lock)
(require 'map)
(defface all-the-icons-dired-dir-face
'((t (:inherit dired-directory)))
"Face for the directory icon."
:group 'all-the-icons-faces)
(defcustom all-the-icons-dired-lighter " all-the-icons-dired-mode"
"Lighter of all-the-icons-dired-mode"
:group 'all-the-icons
:type 'string)
(defcustom all-the-icons-dired-v-adjust 0.01
"The default vertical adjustment of the icon in the Dired buffer."
:group 'all-the-icons
:type 'number)
(defcustom all-the-icons-dired-monochrome t
"Whether to show the icons as the same color as the text on the same line."
:group 'all-the-icons
:type 'boolean)
(defvar all-the-icons-dired-mode)
(defun all-the-icons-dired--icon (file)
"Return the icon for FILE."
(if (file-directory-p file)
(all-the-icons-icon-for-dir file
:face 'all-the-icons-dired-dir-face
:v-adjust all-the-icons-dired-v-adjust)
(apply 'all-the-icons-icon-for-file file
(append
`(:v-adjust ,all-the-icons-dired-v-adjust)
(when all-the-icons-dired-monochrome
`(:face ,(face-at-point)))))))
(defun all-the-icons-dired--put-icon (pos)
"Propertize POS with icon."
(let* ((file (dired-get-filename 'relative 'noerror))
(icon (all-the-icons-dired--icon file))
(image (copy-sequence (get-text-property 0 'display icon)))
(props (map-delete (copy-sequence (text-properties-at 0 icon)) 'display)))
(if (or (not (eq (car image) 'image)) (member file '("." "..")))
(put-text-property (1- pos) pos 'display
(if (member file '("." ".."))
" "
(concat " " icon " ")))
(setf (image-property image :margin) (cons (/ (window-text-width nil t) (window-text-width)) 0))
(add-text-properties (1- pos) pos (append props `(display ,image) )))))
(defun all-the-icons-dired--fontify-region (start end &optional loudly)
"Add icons using text properties from START to END.
START, END and the optional argument LOUDLY is passed to
`font-lock-default-fontify-region'."
(let ((extended-region (font-lock-default-fontify-region start end loudly)))
(when (and (consp extended-region)
(eq (car extended-region) 'jit-lock-bounds))
(setq start (cadr extended-region))
(setq end (cddr extended-region)))
(with-silent-modifications
(save-excursion
(goto-char start)
(while (< (point) end)
(when-let ((pos (dired-move-to-filename)))
(all-the-icons-dired--put-icon pos))
(forward-line 1))))
extended-region))
(defun all-the-icons-dired--setup ()
"Set up `all-the-icons-dired'."
(add-function :override (local 'font-lock-fontify-region-function) #'all-the-icons-dired--fontify-region)
(setq-local font-lock-extra-managed-props (cons 'display font-lock-extra-managed-props))
(cond (jit-lock-mode
(jit-lock-refontify))
(font-lock-mode
(font-lock-fontify-region (point-min) (point-max)))))
(defun all-the-icons-dired--teardown ()
"Tear down `all-the-icons-dired'."
(font-lock-unfontify-buffer)
(remove-function (local 'font-lock-fontify-region-function) #'all-the-icons-dired--fontify-region)
(setq-local font-lock-extra-managed-props (remove 'display font-lock-extra-managed-props))
(cond (jit-lock-mode
(jit-lock-refontify))
(font-lock-mode
(font-lock-fontify-region (point-min) (point-max)))))
;;;###autoload
(define-minor-mode all-the-icons-dired-mode
"Display all-the-icons icon for each file in a Dired buffer."
:lighter all-the-icons-dired-lighter
(when (derived-mode-p 'dired-mode)
(if all-the-icons-dired-mode
(all-the-icons-dired--setup)
(all-the-icons-dired--teardown))))
(provide 'all-the-icons-dired)
;;; all-the-icons-dired.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2022-11-07T17:05:03-0500 using RSA

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2022-11-07T17:05:05-0500 using RSA

View file

@ -0,0 +1,210 @@
;;; async-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from async.el
(autoload 'async-start-process "async" "\
Start the executable PROGRAM asynchronously named NAME. See `async-start'.
PROGRAM is passed PROGRAM-ARGS, calling FINISH-FUNC with the
process object when done. If FINISH-FUNC is nil, the future
object will return the process object when the program is
finished. Set DEFAULT-DIRECTORY to change PROGRAM's current
working directory.
(fn NAME PROGRAM FINISH-FUNC &rest PROGRAM-ARGS)")
(autoload 'async-start "async" "\
Execute START-FUNC (often a lambda) in a subordinate Emacs process.
When done, the return value is passed to FINISH-FUNC. Example:
(async-start
;; What to do in the child process
(lambda ()
(message \"This is a test\")
(sleep-for 3)
222)
;; What to do when it finishes
(lambda (result)
(message \"Async process done, result should be 222: %s\"
result)))
If you call `async-send' from a child process, the message will
be also passed to the FINISH-FUNC. You can test RESULT to see if
it is a message by using `async-message-p'. If nil, it means
this is the final result. Example of the FINISH-FUNC:
(lambda (result)
(if (async-message-p result)
(message \"Received a message from child process: %s\" result)
(message \"Async process done, result: %s\" result)))
If FINISH-FUNC is nil or missing, a future is returned that can
be inspected using `async-get', blocking until the value is
ready. Example:
(let ((proc (async-start
;; What to do in the child process
(lambda ()
(message \"This is a test\")
(sleep-for 3)
222))))
(message \"I'm going to do some work here\") ;; ....
(message \"Waiting on async process, result should be 222: %s\"
(async-get proc)))
If you don't want to use a callback, and you don't care about any
return value from the child process, pass the `ignore' symbol as
the second argument (if you don't, and never call `async-get', it
will leave *emacs* process buffers hanging around):
(async-start
(lambda ()
(delete-file \"a remote file on a slow link\" nil))
\\='ignore)
Special case:
If the output of START-FUNC is a string with properties
e.g. (buffer-string) RESULT will be transformed in a list where the
car is the string itself (without props) and the cdr the rest of
properties, this allows using in FINISH-FUNC the string without
properties and then apply the properties in cdr to this string (if
needed).
Properties handling special objects like markers are returned as
list to allow restoring them later.
See <https://github.com/jwiegley/emacs-async/issues/145> for more infos.
Note: Even when FINISH-FUNC is present, a future is still
returned except that it yields no value (since the value is
passed to FINISH-FUNC). Call `async-get' on such a future always
returns nil. It can still be useful, however, as an argument to
`async-ready' or `async-wait'.
(fn START-FUNC &optional FINISH-FUNC)")
(register-definition-prefixes "async" '("async-"))
;;; Generated autoloads from async-bytecomp.el
(autoload 'async-byte-recompile-directory "async-bytecomp" "\
Compile all *.el files in DIRECTORY asynchronously.
All *.elc files are systematically deleted before proceeding.
(fn DIRECTORY &optional QUIET)")
(defvar async-bytecomp-package-mode nil "\
Non-nil if Async-Bytecomp-Package mode is enabled.
See the `async-bytecomp-package-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `async-bytecomp-package-mode'.")
(custom-autoload 'async-bytecomp-package-mode "async-bytecomp" nil)
(autoload 'async-bytecomp-package-mode "async-bytecomp" "\
Byte compile asynchronously packages installed with package.el.
Async compilation of packages can be controlled by
`async-bytecomp-allowed-packages'.
This is a global minor mode. If called interactively, toggle the
`Async-Bytecomp-Package mode' mode. If the prefix argument is
positive, enable the mode, and if it is zero or negative, disable
the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `(default-value \\='async-bytecomp-package-mode)'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(autoload 'async-byte-compile-file "async-bytecomp" "\
Byte compile Lisp code FILE asynchronously.
Same as `byte-compile-file' but asynchronous.
(fn FILE)" t)
(register-definition-prefixes "async-bytecomp" '("async-"))
;;; Generated autoloads from async-package.el
(register-definition-prefixes "async-package" '("async-package-"))
;;; Generated autoloads from dired-async.el
(defvar dired-async-mode nil "\
Non-nil if Dired-Async mode is enabled.
See the `dired-async-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `dired-async-mode'.")
(custom-autoload 'dired-async-mode "dired-async" nil)
(autoload 'dired-async-mode "dired-async" "\
Do dired actions asynchronously.
This is a global minor mode. If called interactively, toggle the
`Dired-Async mode' mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `(default-value \\='dired-async-mode)'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(autoload 'dired-async-do-copy "dired-async" "\
Run dired-do-copy asynchronously.
(fn &optional ARG)" t)
(autoload 'dired-async-do-symlink "dired-async" "\
Run dired-do-symlink asynchronously.
(fn &optional ARG)" t)
(autoload 'dired-async-do-hardlink "dired-async" "\
Run dired-do-hardlink asynchronously.
(fn &optional ARG)" t)
(autoload 'dired-async-do-rename "dired-async" "\
Run dired-do-rename asynchronously.
(fn &optional ARG)" t)
(register-definition-prefixes "dired-async" '("dired-async-"))
;;; Generated autoloads from smtpmail-async.el
(register-definition-prefixes "smtpmail-async" '("async-smtpmail-"))
;;; End of scraped data
(provide 'async-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; async-autoloads.el ends here

View file

@ -0,0 +1,201 @@
;;; async-bytecomp.el --- Compile elisp files asynchronously -*- lexical-binding: t -*-
;; Copyright (C) 2014-2022 Free Software Foundation, Inc.
;; Authors: John Wiegley <jwiegley@gmail.com>
;; Thierry Volpiatto <thievol@posteo.net>
;; Keywords: dired async byte-compile
;; X-URL: https://github.com/jwiegley/emacs-async
;; 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:
;;
;; This package provide the `async-byte-recompile-directory' function
;; which allows, as the name says to recompile a directory outside of
;; your running emacs.
;; The benefit is your files will be compiled in a clean environment without
;; the old *.el files loaded.
;; Among other things, this fix a bug in package.el which recompile
;; the new files in the current environment with the old files loaded, creating
;; errors in most packages after upgrades.
;;
;; NB: This package is advising the function `package--compile'.
;;; Code:
(require 'cl-lib)
(require 'async)
(require 'bytecomp)
(declare-function package-desc-name "package.el")
(declare-function package-desc-dir "package.el")
(defcustom async-bytecomp-allowed-packages 'all
"Packages in this list will be compiled asynchronously by `package--compile'.
All the dependencies of these packages will be compiled async too,
so no need to add dependencies to this list.
The value of this variable can also be the symbol `all' (default), in this case
all packages are always compiled asynchronously."
:group 'async
:type '(choice
(const :tag "All packages" all)
(repeat symbol)))
(defvar async-byte-compile-log-file
(concat user-emacs-directory "async-bytecomp.log"))
(defvar async-bytecomp-load-variable-regexp "\\`load-path\\'"
"The variable used by `async-inject-variables' when (re)compiling async.")
(defun async-bytecomp--file-to-comp-buffer (file-or-dir &optional quiet type)
(let ((bn (file-name-nondirectory file-or-dir))
(action-name (pcase type
('file "File")
('directory "Directory"))))
(if (file-exists-p async-byte-compile-log-file)
(let ((buf (get-buffer-create byte-compile-log-buffer))
(n 0))
(with-current-buffer buf
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert-file-contents async-byte-compile-log-file)
(compilation-mode))
(display-buffer buf)
(delete-file async-byte-compile-log-file)
(unless quiet
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^.*:Error:" nil t)
(cl-incf n)))
(if (> n 0)
(message "Failed to compile %d files in directory `%s'" n bn)
(message "%s `%s' compiled asynchronously with warnings"
action-name bn)))))
(unless quiet
(message "%s `%s' compiled asynchronously with success" action-name bn)))))
;;;###autoload
(defun async-byte-recompile-directory (directory &optional quiet)
"Compile all *.el files in DIRECTORY asynchronously.
All *.elc files are systematically deleted before proceeding."
(cl-loop with dir = (directory-files directory t "\\.elc\\'")
unless dir return nil
for f in dir
when (file-exists-p f) do (delete-file f))
;; Ensure async is reloaded when async.elc is deleted.
;; This happen when recompiling its own directory.
(load "async")
(let ((call-back
(lambda (&optional _ignore)
(async-bytecomp--file-to-comp-buffer directory quiet 'directory))))
(async-start
`(lambda ()
(require 'bytecomp)
,(async-inject-variables async-bytecomp-load-variable-regexp)
(let ((default-directory (file-name-as-directory ,directory))
error-data)
(add-to-list 'load-path default-directory)
(byte-recompile-directory ,directory 0 t)
(when (get-buffer byte-compile-log-buffer)
(setq error-data (with-current-buffer byte-compile-log-buffer
(buffer-substring-no-properties (point-min) (point-max))))
(unless (string= error-data "")
(with-temp-file ,async-byte-compile-log-file
(erase-buffer)
(insert error-data))))))
call-back)
(unless quiet (message "Started compiling asynchronously directory %s" directory))))
(defvar package-archive-contents)
(defvar package-alist)
(declare-function package-desc-reqs "package.el" (cl-x))
(defun async-bytecomp--get-package-deps (pkgs)
;; Same as `package--get-deps' but parse instead `package-archive-contents'
;; because PKG is not already installed and not present in `package-alist'.
;; However fallback to `package-alist' in case PKG no more present
;; in `package-archive-contents' due to modification to `package-archives'.
;; See issue #58.
(let ((seen '()))
(while pkgs
(let ((pkg (pop pkgs)))
(unless (memq pkg seen)
(let ((pkg-desc (cadr (or (assq pkg package-archive-contents)
(assq pkg package-alist)))))
(when pkg-desc
(push pkg seen)
(setq pkgs (append (mapcar #'car (package-desc-reqs pkg-desc))
pkgs)))))))
seen))
(defun async--package-compile (orig-fun pkg-desc &rest args)
(let ((cur-package (package-desc-name pkg-desc))
(pkg-dir (package-desc-dir pkg-desc)))
(if (or (member async-bytecomp-allowed-packages '(t all (all)))
(memq cur-package (async-bytecomp--get-package-deps
async-bytecomp-allowed-packages)))
(progn
(when (eq cur-package 'async)
(fmakunbound 'async-byte-recompile-directory)
;; Add to `load-path' the latest version of async and
;; reload it when reinstalling async.
(cl-pushnew pkg-dir load-path)
(load "async-bytecomp"))
;; `async-byte-recompile-directory' will add directory
;; as needed to `load-path'.
(async-byte-recompile-directory (package-desc-dir pkg-desc) t))
(apply orig-fun pkg-desc args))))
;;;###autoload
(define-minor-mode async-bytecomp-package-mode
"Byte compile asynchronously packages installed with package.el.
Async compilation of packages can be controlled by
`async-bytecomp-allowed-packages'."
:group 'async
:global t
(if async-bytecomp-package-mode
(advice-add 'package--compile :around #'async--package-compile)
(advice-remove 'package--compile #'async--package-compile)))
;;;###autoload
(defun async-byte-compile-file (file)
"Byte compile Lisp code FILE asynchronously.
Same as `byte-compile-file' but asynchronous."
(interactive "fFile: ")
(let ((call-back
(lambda (&optional _ignore)
(async-bytecomp--file-to-comp-buffer file nil 'file))))
(async-start
`(lambda ()
(require 'bytecomp)
,(async-inject-variables async-bytecomp-load-variable-regexp)
(let ((default-directory ,(file-name-directory file))
error-data)
(add-to-list 'load-path default-directory)
(byte-compile-file ,file)
(when (get-buffer byte-compile-log-buffer)
(setq error-data (with-current-buffer byte-compile-log-buffer
(buffer-substring-no-properties (point-min) (point-max))))
(unless (string= error-data "")
(with-temp-file ,async-byte-compile-log-file
(erase-buffer)
(insert error-data))))))
call-back)))
(provide 'async-bytecomp)
;;; async-bytecomp.el ends here

View file

@ -0,0 +1,132 @@
;;; async-package.el --- Fetch packages asynchronously -*- lexical-binding: t -*-
;; Copyright (C) 2014-2022 Free Software Foundation, Inc.
;; Author: Thierry Volpiatto <thievol@posteo.net>
;; Keywords: dired async byte-compile package
;; X-URL: https://github.com/jwiegley/emacs-async
;; 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:
;; Provide the function `async-package-do-action' to
;; (re)install/upgrade packages asynchronously.
;;; Code:
(eval-when-compile (require 'cl-lib))
(require 'async-bytecomp)
(require 'dired-async)
(require 'package)
(define-minor-mode async-package--modeline-mode
"Notify mode-line that an async process run."
:group 'async
:global t
:lighter (:eval (propertize (format " [%s async job Installing package(s)]"
(length (dired-async-processes
'async-pkg-install)))
'face 'async-package-message))
(unless async-package--modeline-mode
(let ((visible-bell t)) (ding))))
(defface async-package-message
'((t (:foreground "yellow")))
"Face used for mode-line message.")
(defun async-package-do-action (action packages error-file)
"Execute ACTION asynchronously on PACKAGES.
Argument ACTION can be one of \\='install, \\='upgrade, \\='reinstall.
Argument PACKAGES is a list of packages (symbols).
Argument ERROR-FILE is the file where errors are logged, if some."
(require 'async-bytecomp)
(let ((fn (pcase action
('install 'package-install)
('upgrade 'package-upgrade)
('reinstall 'package-reinstall)))
(action-string (pcase action
('install "Installing")
('upgrade "Upgrading")
('reinstall "Reinstalling"))))
(message "%s %s package(s)..." action-string (length packages))
(process-put
(async-start
`(lambda ()
(require 'bytecomp)
(setq package-archives ',package-archives
package-pinned-packages ',package-pinned-packages
package-archive-contents ',package-archive-contents
package-alist ',package-alist
load-path ',load-path)
(prog1
(condition-case err
(mapc ',fn ',packages)
(error
(with-temp-file ,error-file
(insert
(format
"%S:\n Please refresh package list before %s"
err ,action-string)))))
(let (error-data)
(when (get-buffer byte-compile-log-buffer)
(setq error-data (with-current-buffer byte-compile-log-buffer
(buffer-substring-no-properties
(point-min) (point-max))))
(unless (string= error-data "")
(with-temp-file ,async-byte-compile-log-file
(erase-buffer)
(insert error-data)))))))
(lambda (result)
(if (file-exists-p error-file)
(let ((buf (find-file-noselect error-file)))
(pop-to-buffer
buf '(nil . ((window-height . fit-window-to-buffer))))
(special-mode)
(delete-file error-file)
(async-package--modeline-mode -1))
(when result
(let ((pkgs (if (listp result) result (list result))))
(when (eq action 'install)
(customize-save-variable
'package-selected-packages
(delete-dups (append pkgs package-selected-packages))))
(package-load-all-descriptors) ; refresh package-alist.
(mapc #'package-activate pkgs) ; load packages.
(async-package--modeline-mode -1)
(message "%s %s packages done" action-string (length packages))
(run-with-timer
0.1 nil
(lambda (lst str)
(dired-async-mode-line-message
"%s %d package(s) done"
'async-package-message
str (length lst)))
packages action-string)
(when (file-exists-p async-byte-compile-log-file)
(let ((buf (get-buffer-create byte-compile-log-buffer)))
(with-current-buffer buf
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert-file-contents async-byte-compile-log-file)
(compilation-mode))
(display-buffer buf)
(delete-file async-byte-compile-log-file)))))))))
'async-pkg-install t)
(async-package--modeline-mode 1)))
(provide 'async-package)
;;; async-package.el ends here

View file

@ -0,0 +1,14 @@
(define-package "async" "20240719.640" "Asynchronous processing in Emacs"
'((emacs "24.4"))
:commit "93957f5b0f2ed22aeef02972de860fb9c45b4422" :authors
'(("John Wiegley" . "jwiegley@gmail.com"))
:maintainers
'(("Thierry Volpiatto" . "thievol@posteo.net"))
:maintainer
'("Thierry Volpiatto" . "thievol@posteo.net")
:keywords
'("async")
:url "https://github.com/jwiegley/emacs-async")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,609 @@
;;; async.el --- Asynchronous processing in Emacs -*- lexical-binding: t -*-
;; Copyright (C) 2012-2022 Free Software Foundation, Inc.
;; Author: John Wiegley <jwiegley@gmail.com>
;; Maintainer: Thierry Volpiatto <thievol@posteo.net>
;; Created: 18 Jun 2012
;; Version: 1.9.8
;; Package-Requires: ((emacs "24.4"))
;; Keywords: async
;; X-URL: https://github.com/jwiegley/emacs-async
;; 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:
;; Adds the ability to call asynchronous functions and process with ease. See
;; the documentation for `async-start' and `async-start-process'.
;;; Code:
(eval-when-compile (require 'cl-lib))
(defvar tramp-password-prompt-regexp)
(defgroup async nil
"Simple asynchronous processing in Emacs"
:group 'lisp)
(defcustom async-variables-noprops-function #'async--purecopy
"Default function to remove text properties in variables."
:type 'function)
(defcustom async-prompt-for-password t
"Prompt for password in parent Emacs if needed when non nil.
When this is nil child Emacs will hang forever when a user interaction
for password is required unless a password is stored in a \".authinfo\" file."
:type 'boolean)
(defvar async-process-noquery-on-exit nil
"Used as the :noquery argument to `make-process'.
Intended to be let-bound around a call to `async-start' or
`async-start-process'. If non-nil, the child Emacs process will
be silently killed if the user exits the parent Emacs.")
(defvar async-debug nil)
(defvar async-send-over-pipe t)
(defvar async-in-child-emacs nil)
(defvar async-callback nil)
(defvar async-callback-for-process nil
"Non-nil if the subprocess is not Emacs executing a lisp form.")
(defvar async-callback-value nil)
(defvar async-callback-value-set nil)
(defvar async-current-process nil)
(defvar async--procvar nil)
(defvar async-read-marker nil
"Position from which we read the last message packet.
Message packets are delivered from client line-by-line as base64
encoded strings.")
(defvar async-child-init nil
"Initialisation file for async child Emacs.
If defined this allows for an init file to setup the child Emacs. It
should not be your normal init.el as that would likely load more
things that you require. It should limit itself to ensuring paths have
been setup so any async code can load libraries you expect.")
;; For emacs<29 (only exists in emacs-29+).
(defvar print-symbols-bare)
(defun async--purecopy (object)
"Remove text properties in OBJECT.
Argument OBJECT may be a list or a string, if anything else it
is returned unmodified."
(cond ((stringp object)
(substring-no-properties object))
((consp object)
(cl-loop for elm in object
;; A string.
if (stringp elm)
collect (substring-no-properties elm)
else
;; Proper lists.
if (and (consp elm) (null (cdr (last elm))))
collect (async--purecopy elm)
else
;; Dotted lists.
;; We handle here only dotted list where car and cdr
;; are atoms i.e. (x . y) and not (x . (x . y)) or
;; (x . (x y)) which should fit most cases.
if (and (consp elm) (cdr (last elm)))
collect (let ((key (car elm))
(val (cdr elm)))
(cons (if (stringp key)
(substring-no-properties key)
key)
(if (stringp val)
(substring-no-properties val)
val)))
else
collect elm))
(t object)))
(defvar async-inject-variables-exclude-regexps '("-syntax-table\\'")
"A list of regexps that `async-inject-variables' should ignore.")
(defun async-inject-variables
(include-regexp &optional predicate exclude-regexp noprops)
"Return a `setq' form that replicates part of the calling environment.
It sets the value for every variable matching INCLUDE-REGEXP and
also PREDICATE. It will not perform injection for any variable
matching EXCLUDE-REGEXP (if present) and variables matching one of
`async-inject-variables-exclude-regexps'.
When NOPROPS is non nil it tries to strip out text properties of each
variable's value with `async-variables-noprops-function'.
It is intended to be used as follows:
(async-start
\\=`(lambda ()
(require \\='smtpmail)
(with-temp-buffer
(insert ,(buffer-substring-no-properties (point-min) (point-max)))
;; Pass in the variable environment for smtpmail
,(async-inject-variables \"\\\\=`\\(smtpmail\\|\\(user-\\)?mail\\)-\")
(smtpmail-send-it)))
\\='ignore)"
`(setq
,@(let (bindings)
(mapatoms
(lambda (sym)
(let ((sname (and (boundp sym) (symbol-name sym)))
value)
(when (and sname
(or (null include-regexp)
(string-match include-regexp sname))
(or (null exclude-regexp)
(not (string-match exclude-regexp sname)))
(cl-loop for re in async-inject-variables-exclude-regexps
never (string-match-p re sname)))
(setq value (symbol-value sym))
(unless (or (stringp value)
(memq value '(nil t))
(numberp value)
(vectorp value))
(setq value `(quote ,value)))
(when noprops
(setq value (funcall async-variables-noprops-function
value)))
(when (or (null predicate)
(funcall predicate sym))
(setq bindings (cons value bindings)
bindings (cons sym bindings)))))))
bindings)))
(defalias 'async-inject-environment 'async-inject-variables)
(defun async-handle-result (func result buf)
(if (null func)
(progn
(set (make-local-variable 'async-callback-value) result)
(set (make-local-variable 'async-callback-value-set) t))
(unwind-protect
(if (and (listp result)
(eq 'async-signal (nth 0 result)))
(signal (car (nth 1 result))
(cdr (nth 1 result)))
(funcall func result))
(unless async-debug
(kill-buffer buf)))))
(defun async-when-done (proc &optional _change)
"Process sentinel used to retrieve the value from the child process."
(when (eq 'exit (process-status proc))
(with-current-buffer (process-buffer proc)
(let ((async-current-process proc))
(if (= 0 (process-exit-status proc))
(if async-callback-for-process
(if async-callback
(prog1
(funcall async-callback proc)
(unless async-debug
;; we need to check this because theoretically
;; `async-callback' could've killed it already
(when (buffer-live-p (process-buffer proc))
(kill-buffer (process-buffer proc)))))
(set (make-local-variable 'async-callback-value) proc)
(set (make-local-variable 'async-callback-value-set) t))
;; Maybe strip out unreadable "#"; They are replaced by
;; empty string unless they are prefixing a special
;; object like a marker. See issue #145.
(widen)
(goto-char (point-min))
(save-excursion
;; Transform markers in list like
;; (marker (moves after insertion) at 2338 in
;; test\.org) so that remap text properties function
;; can parse it to restitute marker.
(while (re-search-forward "#<\\([^>]*\\)>" nil t)
(replace-match (concat "(" (match-string 1) ")") t t)))
(while (re-search-forward "#(" nil t)
(replace-match "(" t t))
(goto-char (point-max))
(backward-sexp)
(let ((value (read (current-buffer))))
(async-handle-result async-callback value (current-buffer))))
(set (make-local-variable 'async-callback-value)
(list 'error
(format "Async process '%s' failed with exit code %d"
(process-name proc) (process-exit-status proc))))
(set (make-local-variable 'async-callback-value-set) t))))))
(defun async-read-from-client (proc string &optional prompt-for-pwd)
"Process text from client process.
The string chunks usually arrive in maximum of 4096 bytes, so a
long client message might be split into multiple calls of this
function.
We use a marker `async-read-marker' to track the position of the
lasts complete line. Every time we get new input, we try to look
for newline, and if found, process the entire line and bump the
marker position to the end of this next line.
Argument PROMPT-FOR-PWD allow binding lexically the value of
`async-prompt-for-password', if unspecified its global value
is used."
(with-current-buffer (process-buffer proc)
(when (and prompt-for-pwd
(boundp 'tramp-password-prompt-regexp)
tramp-password-prompt-regexp
(string-match tramp-password-prompt-regexp string))
(process-send-string
proc (concat (read-passwd (match-string 0 string)) "\n")))
(goto-char (point-max))
(save-excursion
(insert string))
(while (search-forward "\n" nil t)
(save-excursion
(save-restriction
(widen)
(narrow-to-region async-read-marker (point))
(goto-char (point-min))
(let (msg)
(condition-case nil
;; It is safe to throw errors in the read because we
;; send messages always on their own line, and they
;; are always a base64 encoded string, so a message
;; will always read. We will also ignore the rest
;; of this line since there won't be anything
;; interesting.
(while (setq msg (read (current-buffer)))
(let ((msg-decoded (ignore-errors (base64-decode-string msg))))
(when msg-decoded
(setq msg-decoded (car (read-from-string msg-decoded)))
(when (and (listp msg-decoded)
(async-message-p msg-decoded)
async-callback)
(funcall async-callback msg-decoded)))))
;; This is OK, we reached the end of the chunk subprocess sent
;; at this time.
(invalid-read-syntax t)
(end-of-file t)))
(goto-char (point-max))
(move-marker async-read-marker (point)))))))
(defun async--receive-sexp (&optional stream)
;; FIXME: Why use `utf-8-auto' instead of `utf-8-unix'? This is
;; a communication channel over which we have complete control,
;; so we get to choose exactly which encoding and EOL we use, isn't
;; it?
;; UPDATE: We use now `utf-8-emacs-unix' instead of `utf-8-auto' as
;; recommended in bug#165.
(let ((sexp (decode-coding-string (base64-decode-string (read stream))
'utf-8-emacs-unix))
;; Parent expects UTF-8 encoded text.
(coding-system-for-write 'utf-8-emacs-unix))
(if async-debug
(message "Received sexp {{{%s}}}" (pp-to-string sexp)))
(setq sexp (read sexp))
(if async-debug
(message "Read sexp {{{%s}}}" (pp-to-string sexp)))
(eval sexp t)))
(defun async--insert-sexp (sexp)
(let (print-level
print-length
(print-escape-nonascii t)
(print-circle t)
;; Fix bug#153 in emacs-29 with symbol's positions.
(print-symbols-bare t))
(prin1 sexp (current-buffer))
;; Just in case the string we're sending might contain EOF
(encode-coding-region (point-min) (point-max) 'utf-8-emacs-unix)
(base64-encode-region (point-min) (point-max) t)
(goto-char (point-min)) (insert ?\")
(goto-char (point-max)) (insert ?\" ?\n)))
(defun async--transmit-sexp (process sexp)
(with-temp-buffer
(if async-debug
(message "Transmitting sexp {{{%s}}}" (pp-to-string sexp)))
(async--insert-sexp sexp)
(process-send-region process (point-min) (point-max))))
(defun async-batch-invoke ()
"Called from the child Emacs process' command line."
;; Make sure 'message' and 'prin1' encode stuff in UTF-8, as parent
;; process expects.
(let ((coding-system-for-write 'utf-8-emacs-unix)
(args-left command-line-args-left))
(setq async-in-child-emacs t
debug-on-error async-debug
command-line-args-left nil)
(condition-case-unless-debug err
(let ((ret (funcall
(async--receive-sexp (unless async-send-over-pipe
args-left)))))
;; The newlines makes client messages more robust and also
;; handle some weird line-buffering issues on windows.
;; Sometimes, the last "chunk" was not read by the filter,
;; so a newline here should force a buffer flush.
(princ "\n")
(prin1 ret)
(princ "\n"))
(error
(progn
(princ "\n")
(prin1 (list 'async-signal err))
(princ "\n"))))))
(defun async-ready (future)
"Query a FUTURE to see if it is ready.
I.e., if no blocking would result from a call to `async-get' on that FUTURE."
(and (memq (process-status future) '(exit signal))
(let ((buf (process-buffer future)))
(if (buffer-live-p buf)
(with-current-buffer buf
async-callback-value-set)
t))))
(defun async-wait (future)
"Wait for FUTURE to become ready."
(while (not (async-ready future))
(sleep-for 0.05)))
(defun async-get (future)
"Get the value from process FUTURE when it is ready.
FUTURE is returned by `async-start' or `async-start-process' when
its FINISH-FUNC is nil."
(and future (async-wait future))
(let ((buf (process-buffer future)))
(when (buffer-live-p buf)
(with-current-buffer buf
(async-handle-result
#'identity async-callback-value (current-buffer))))))
(defun async-message-p (value)
"Return non-nil if VALUE is an async.el message packet."
(and (listp value)
(plist-get value :async-message)))
(defun async-send (process-or-key &rest args)
"Send the given message to the asynchronous child or parent Emacs.
To send messages from the parent to a child, PROCESS-OR-KEY is
the child process object. ARGS is a plist. Example:
(async-send proc :operation :load-file :file \"this file\")
To send messages from the child to the parent, PROCESS-OR-KEY is
the first key of the plist, ARGS is a value followed by
optionally more key-value pairs. Example:
(async-send :status \"finished\" :file-size 123)"
(let ((args (append args '(:async-message t))))
(if async-in-child-emacs
;; `princ' because async--insert-sexp already quotes everything.
(princ
(with-temp-buffer
(async--insert-sexp (cons process-or-key args))
;; always make sure that one message package has its own
;; line as there can be any random debug garbage printed
;; above it.
(concat "\n" (buffer-string))))
(async--transmit-sexp process-or-key (list 'quote args)))))
(defun async-receive ()
"Receive message from parent Emacs.
The child process blocks until a message is received.
Message is a plist with one key :async-message set to t always
automatically added to signify this plist is an async message.
You can use `async-message-p' to test if the payload was a
message.
Use
(let ((msg (async-receive))) ...)
to read and process a message."
(async--receive-sexp))
;;;###autoload
(defun async-start-process (name program finish-func &rest program-args)
"Start the executable PROGRAM asynchronously named NAME. See `async-start'.
PROGRAM is passed PROGRAM-ARGS, calling FINISH-FUNC with the
process object when done. If FINISH-FUNC is nil, the future
object will return the process object when the program is
finished. Set DEFAULT-DIRECTORY to change PROGRAM's current
working directory."
(let* ((buf (generate-new-buffer (concat "*" name "*")))
(buf-err (generate-new-buffer (concat "*" name ":err*")))
(prt-for-pwd async-prompt-for-password)
(proc (let ((process-connection-type nil))
(make-process
:name name
:buffer buf
:stderr buf-err
:command (cons program program-args)
:noquery async-process-noquery-on-exit))))
(set-process-sentinel
(get-buffer-process buf-err)
(lambda (proc _change)
(unless (or async-debug (process-live-p proc))
(kill-buffer (process-buffer proc)))))
(with-current-buffer buf
(set (make-local-variable 'async-callback) finish-func)
(set (make-local-variable 'async-read-marker)
(set-marker (make-marker) (point-min) buf))
(set-marker-insertion-type async-read-marker nil)
(set-process-sentinel proc #'async-when-done)
;; Pass the value of `async-prompt-for-password' to the process
;; filter fn through the lexical local var prt-for-pwd (Issue#182).
(set-process-filter proc (lambda (proc string)
(async-read-from-client
proc string prt-for-pwd)))
(unless (string= name "emacs")
(set (make-local-variable 'async-callback-for-process) t))
proc)))
(defvar async-quiet-switch "-Q"
"The Emacs parameter to use to call emacs without config.
Can be one of \"-Q\" or \"-q\".
Default is \"-Q\" but it is sometimes useful to use \"-q\" to have a
enhanced config or some more variables loaded.")
(defun async--emacs-program-args (&optional sexp)
"Return a list of arguments for invoking the child Emacs."
;; Using `locate-library' ensure we use the right file
;; when the .elc have been deleted.
(let ((args (list async-quiet-switch "-l" (locate-library "async"))))
(when async-child-init
(setq args (append args (list "-l" async-child-init))))
(append args (list "-batch" "-f" "async-batch-invoke"
(if sexp
(with-temp-buffer
(async--insert-sexp (list 'quote sexp))
(buffer-string))
"<none>")))))
;;;###autoload
(defun async-start (start-func &optional finish-func)
"Execute START-FUNC (often a lambda) in a subordinate Emacs process.
When done, the return value is passed to FINISH-FUNC. Example:
(async-start
;; What to do in the child process
(lambda ()
(message \"This is a test\")
(sleep-for 3)
222)
;; What to do when it finishes
(lambda (result)
(message \"Async process done, result should be 222: %s\"
result)))
If you call `async-send' from a child process, the message will
be also passed to the FINISH-FUNC. You can test RESULT to see if
it is a message by using `async-message-p'. If nil, it means
this is the final result. Example of the FINISH-FUNC:
(lambda (result)
(if (async-message-p result)
(message \"Received a message from child process: %s\" result)
(message \"Async process done, result: %s\" result)))
If FINISH-FUNC is nil or missing, a future is returned that can
be inspected using `async-get', blocking until the value is
ready. Example:
(let ((proc (async-start
;; What to do in the child process
(lambda ()
(message \"This is a test\")
(sleep-for 3)
222))))
(message \"I'm going to do some work here\") ;; ....
(message \"Waiting on async process, result should be 222: %s\"
(async-get proc)))
If you don't want to use a callback, and you don't care about any
return value from the child process, pass the `ignore' symbol as
the second argument (if you don't, and never call `async-get', it
will leave *emacs* process buffers hanging around):
(async-start
(lambda ()
(delete-file \"a remote file on a slow link\" nil))
\\='ignore)
Special case:
If the output of START-FUNC is a string with properties
e.g. (buffer-string) RESULT will be transformed in a list where the
car is the string itself (without props) and the cdr the rest of
properties, this allows using in FINISH-FUNC the string without
properties and then apply the properties in cdr to this string (if
needed).
Properties handling special objects like markers are returned as
list to allow restoring them later.
See <https://github.com/jwiegley/emacs-async/issues/145> for more infos.
Note: Even when FINISH-FUNC is present, a future is still
returned except that it yields no value (since the value is
passed to FINISH-FUNC). Call `async-get' on such a future always
returns nil. It can still be useful, however, as an argument to
`async-ready' or `async-wait'."
(let ((sexp start-func)
;; Subordinate Emacs will send text encoded in UTF-8.
(coding-system-for-read 'utf-8-emacs-unix))
(setq async--procvar
(apply 'async-start-process
"emacs" (file-truename
(expand-file-name invocation-name
invocation-directory))
finish-func
(async--emacs-program-args (if (not async-send-over-pipe) sexp))))
(if async-send-over-pipe
(async--transmit-sexp async--procvar (list 'quote sexp)))
async--procvar))
(defmacro async-sandbox(func)
"Evaluate FUNC in a separate Emacs process, synchronously."
`(async-get (async-start ,func)))
(defun async--fold-left (fn forms bindings)
(let ((res forms))
(dolist (binding bindings)
(setq res (funcall fn res
(if (listp binding)
binding
(list binding)))))
res))
(defmacro async-let (bindings &rest forms)
"Implements `let', but each binding is established asynchronously.
For example:
(async-let ((x (foo))
(y (bar)))
(message \"%s %s\" x y))
expands to ==>
(async-start (foo)
(lambda (x)
(async-start (bar)
(lambda (y)
(message \"%s %s\" x y)))))"
(declare (indent 1))
(async--fold-left
(lambda (acc binding)
(let ((fun (pcase (cadr binding)
((and (pred functionp) f) f)
(f `(lambda () ,f)))))
`(async-start ,fun
(lambda (,(car binding))
,acc))))
`(progn ,@forms)
(reverse bindings)))
(provide 'async)
;;; async.el ends here

View file

@ -0,0 +1,490 @@
;;; dired-async.el --- Asynchronous dired actions -*- lexical-binding: t -*-
;; Copyright (C) 2012-2019 Free Software Foundation, Inc.
;; Authors: John Wiegley <jwiegley@gmail.com>
;; Thierry Volpiatto <thievol@posteo.net>
;; Keywords: dired async network
;; X-URL: https://github.com/jwiegley/emacs-async
;; 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:
;; This file provide a redefinition of `dired-create-file' function,
;; performs copies, moves and all what is handled by `dired-create-file'
;; in the background using a slave Emacs process,
;; by means of the async.el module.
;; To use it, put this in your .emacs:
;; (dired-async-mode 1)
;; This will enable async copy/rename etc...
;; in dired and helm.
;;; Code:
(require 'cl-lib)
(require 'dired-aux)
(require 'async)
(eval-when-compile
(defvar async-callback))
(defgroup dired-async nil
"Copy rename files asynchronously from dired."
:group 'dired)
(defcustom dired-async-env-variables-regexp
"\\`\\(tramp-\\(default\\|connection\\|remote\\)\\|ange-ftp\\)-.*"
"Variables matching this regexp will be loaded on Child Emacs."
:type 'regexp)
(defcustom dired-async-message-function 'dired-async-mode-line-message
"Function to use to notify result when operation finish.
Should take same args as `message'."
:type 'function)
(defcustom dired-async-log-file "/tmp/dired-async.log"
"File use to communicate errors from Child Emacs to host Emacs."
:type 'string)
(defcustom dired-async-mode-lighter '(:eval
(when (eq major-mode 'dired-mode)
" Async"))
"Mode line lighter used for `dired-async-mode'."
:risky t
:type 'sexp)
(defcustom dired-async-skip-fast nil
"If non-nil, skip async for fast operations.
Same device renames and copying and renaming files smaller than
`dired-async-small-file-max' are considered fast."
:risky t
:type 'boolean)
(defcustom dired-async-small-file-max 5000000
"Files smaller than this in bytes are considered fast to copy
or rename for `dired-async-skip-fast'."
:risky t
:type 'integer)
(defcustom dired-async-large-file-warning-threshold large-file-warning-threshold
"Same as `large-file-warning-threshold' but for dired-async."
:type 'integer)
(defface dired-async-message
'((t (:foreground "yellow")))
"Face used for mode-line message.")
(defface dired-async-failures
'((t (:foreground "red")))
"Face used for mode-line message.")
(defface dired-async-mode-message
'((t (:foreground "Gold")))
"Face used for `dired-async--modeline-mode' lighter.")
(define-minor-mode dired-async--modeline-mode
"Notify mode-line that an async process run."
:global t
:lighter (:eval (propertize (format " [%s Async job(s) running]"
(length (dired-async-processes)))
'face 'dired-async-mode-message))
(unless dired-async--modeline-mode
(let ((visible-bell t)) (ding))))
(defun dired-async-mode-line-message (text face &rest args)
"Notify end of operation in `mode-line'."
(message nil)
(let ((mode-line-format (concat
" " (propertize
(if args
(apply #'format text args)
text)
'face face))))
(force-mode-line-update)
(sit-for 3)
(force-mode-line-update)))
(defun dired-async-processes (&optional propname)
(cl-loop for p in (process-list)
when (process-get p (or propname 'dired-async-process))
collect p))
(defun dired-async-kill-process ()
(interactive)
(let* ((processes (dired-async-processes))
(proc (car (last processes))))
(and proc (delete-process proc))
(unless (> (length processes) 1)
(dired-async--modeline-mode -1))))
(defun dired-async-after-file-create (total operation failures skipped)
"Callback function used for operation handled by `dired-create-file'."
(unless (dired-async-processes)
;; Turn off mode-line notification
;; only when last process end.
(dired-async--modeline-mode -1))
(when operation
(if (file-exists-p dired-async-log-file)
(progn
(pop-to-buffer (get-buffer-create dired-log-buffer))
(goto-char (point-max))
(setq inhibit-read-only t)
(insert "Error: ")
(insert-file-contents dired-async-log-file)
(special-mode)
(shrink-window-if-larger-than-buffer)
(delete-file dired-async-log-file))
(run-with-timer
0.1 nil
(lambda ()
;; First send error messages.
(cond (failures
(funcall dired-async-message-function
"%s failed for %d of %d file%s -- See *Dired log* buffer"
'dired-async-failures
(car operation) (length failures)
total (dired-plural-s total)))
(skipped
(funcall dired-async-message-function
"%s: %d of %d file%s skipped -- See *Dired log* buffer"
'dired-async-failures
(car operation) (length skipped) total
(dired-plural-s total))))
(when dired-buffers
(cl-loop for (_f . b) in dired-buffers
when (buffer-live-p b)
do (with-current-buffer b
(when (and (not (file-remote-p default-directory nil t))
(file-exists-p default-directory))
(revert-buffer nil t)))))
;; Finally send the success message.
(funcall dired-async-message-function
"Asynchronous %s of %s on %s file%s done"
'dired-async-message
(car operation) (cadr operation)
total (dired-plural-s total)))))))
(defun dired-async-maybe-kill-ftp ()
"Return a form to kill ftp process in child emacs."
(quote
(progn
(require 'cl-lib)
(let ((buf (cl-loop for b in (buffer-list)
thereis (and (string-match
"\\`\\*ftp.*"
(buffer-name b)) b))))
(when buf (kill-buffer buf))))))
(defsubst dired-async--directory-p (attributes)
"Return non-nil if ATTRIBUTES is for a directory.
See `file-attributes'."
;; Can also be a string for symlinks, so check for t explicitly.
(eq (file-attribute-type attributes) t))
(defsubst dired-async--same-device-p (f1 f2)
"Return non-nil if F1 and F2 have the same device number."
;; file-attribute-device-number may be a cons cell, so use equal for
;; testing (See Emacs bug/58446).
(equal (file-attribute-device-number (file-attributes f1))
(file-attribute-device-number (file-attributes f2))))
(defun dired-async--small-file-p (file)
"Return non-nil if FILE is considered small.
File is considered small if it size is smaller than
`dired-async-small-file-max'."
(let ((a (file-attributes file)))
;; Directories are always large since we can't easily figure out
;; their total size.
(and (not (dired-async--directory-p a))
(< (file-attribute-size a) dired-async-small-file-max))))
(defun dired-async--skip-async-p (file-creator file name-constructor)
"Return non-nil if we should skip async for FILE.
See `dired-create-files' for FILE-CREATOR and NAME-CONSTRUCTOR."
;; Skip async for small files.
(or (dired-async--small-file-p file)
;; Also skip async for same device renames.
(and (eq file-creator 'dired-rename-file)
(let ((new (funcall name-constructor file)))
(dired-async--same-device-p file (file-name-directory new))))))
(defun dired-async--smart-create-files (old-func file-creator
operation fn-list name-constructor
&optional marker-char)
"Around advice for `dired-create-files'.
Uses async like `dired-async-create-files' but skips certain fast
cases if `dired-async-skip-fast' is non-nil."
(let (async-list quick-list)
(if (or (eq file-creator 'backup-file)
(null dired-async-skip-fast))
(setq async-list fn-list)
(dolist (old fn-list)
(if (dired-async--skip-async-p file-creator old name-constructor)
(push old quick-list)
(push old async-list))))
(when async-list
(dired-async-create-files
file-creator operation (nreverse async-list)
name-constructor marker-char))
(when quick-list
(funcall old-func file-creator operation
(nreverse quick-list) name-constructor marker-char))))
(defun dired-async--abort-if-file-too-large (size op-type filename)
"Warn when FILENAME larger than `dired-async-large-file-warning-threshold'.
Same as `abort-if-file-too-large' but without user-error."
(when (and dired-async-large-file-warning-threshold size
(> size dired-async-large-file-warning-threshold))
(files--ask-user-about-large-file
size op-type filename nil)))
(defvar overwrite-query)
(defun dired-async-create-files (file-creator operation fn-list name-constructor
&optional _marker-char)
"Same as `dired-create-files' but asynchronous.
See `dired-create-files' for the behavior of arguments."
(setq overwrite-query nil)
(let ((total (length fn-list))
failures async-fn-list skipped callback
async-quiet-switch create-dir)
(let (to)
(dolist (from fn-list)
(setq to (funcall name-constructor from))
(if (and (equal to from)
(null (eq file-creator 'backup-file)))
(progn
(setq to nil)
(dired-log "Cannot %s to same file: %s\n"
(downcase operation) from)))
(if (not to)
(setq skipped (cons (dired-make-relative from) skipped))
(let* ((overwrite (and (null (eq file-creator 'backup-file))
(file-exists-p to)))
(dired-overwrite-confirmed ; for dired-handle-overwrite
(and overwrite
(let ((help-form `(format "\
Type SPC or `y' to overwrite file `%s',
DEL or `n' to skip to next,
ESC or `q' to not overwrite any of the remaining files,
`!' to overwrite all remaining files with no more questions." ,to)))
(dired-query 'overwrite-query "Overwrite `%s'?" to)))))
;; Handle the `dired-copy-file' file-creator specially
;; When copying a directory to another directory or
;; possibly to itself or one of its subdirectories.
;; e.g "~/foo/" => "~/test/"
;; or "~/foo/" =>"~/foo/"
;; or "~/foo/ => ~/foo/bar/")
;; In this case the 'name-constructor' have set the destination
;; TO to "~/test/foo" because the old emacs23 behavior
;; of `copy-directory' was to not create the subdirectory
;; and instead copy the contents.
;; With the new behavior of `copy-directory'
;; (similar to the `cp' shell command) we don't
;; need such a construction of the target directory,
;; so modify the destination TO to "~/test/" instead of "~/test/foo/".
(let ((destname (file-name-directory to)))
(when (and (file-directory-p from)
(file-directory-p to)
(eq file-creator 'dired-copy-file))
(setq to destname))
;; If DESTNAME is a subdirectory of FROM, not a symlink,
;; and the method in use is copying, signal an error.
(and (eq t (car (file-attributes destname)))
(eq file-creator 'dired-copy-file)
(file-in-directory-p destname from)
(error "Cannot copy `%s' into its subdirectory `%s'"
from to)))
;; Skip file if it is too large.
(if (and (member operation '("Copy" "Rename"))
(eq (dired-async--abort-if-file-too-large
(file-attribute-size
(file-attributes (file-truename from)))
(downcase operation) from)
'abort))
(push from skipped)
(if overwrite
(or (and dired-overwrite-confirmed
(push (cons from to) async-fn-list))
(progn
(push (dired-make-relative from) failures)
(dired-log "%s `%s' to `%s' failed\n"
operation from to)))
(push (cons from to) async-fn-list))))))
;; Fix tramp issue #80 with emacs-26, use "-q" only when needed.
(setq async-quiet-switch
(if (and (boundp 'tramp-cache-read-persistent-data)
async-fn-list
(cl-loop for (_from . to) in async-fn-list
thereis (file-remote-p to)))
"-q" "-Q"))
;; When failures have been printed to dired log add the date at bob.
(when (or failures skipped) (dired-log t))
;; When async-fn-list is empty that's mean only one file
;; had to be copied and user finally answer NO.
;; In this case async process will never start and callback
;; will have no chance to run, so notify failures here.
(unless async-fn-list
(cond (failures
(funcall dired-async-message-function
"%s failed for %d of %d file%s -- See *Dired log* buffer"
'dired-async-failures
operation (length failures)
total (dired-plural-s total)))
(skipped
(funcall dired-async-message-function
"%s: %d of %d file%s skipped -- See *Dired log* buffer"
'dired-async-failures
operation (length skipped) total
(dired-plural-s total)))))
;; Setup callback.
(setq callback
(lambda (&optional _ignore)
(dired-async-after-file-create
total (list operation (length async-fn-list)) failures skipped)
(when (string= (downcase operation) "rename")
(cl-loop for (file . to) in async-fn-list
for bf = (get-file-buffer file)
for destp = (file-exists-p to)
do (and bf destp
(with-current-buffer bf
(set-visited-file-name to t t)))))))
(let ((dirp (file-directory-p to))
(dest (file-name-directory to)))
(when (boundp 'dired-create-destination-dirs)
(setq create-dir
(cl-case dired-create-destination-dirs
(always 'always)
(ask (and (null dirp)
(null (file-directory-p dest))
(y-or-n-p (format "Create directory `%s'? " dest)))
'always))))))
;; Start async process.
(when async-fn-list
(process-put
(async-start `(lambda ()
(require 'cl-lib) (require 'dired-aux) (require 'dired-x)
,(async-inject-variables dired-async-env-variables-regexp)
(advice-add #'files--ask-user-about-large-file
:override (lambda (&rest args) nil))
(let ((dired-recursive-copies (quote always))
(dired-copy-preserve-time
,dired-copy-preserve-time)
(dired-create-destination-dirs ',create-dir)
(dired-vc-rename-file ,dired-vc-rename-file)
auth-source-save-behavior)
(setq overwrite-backup-query nil)
;; Inline `backup-file' as long as it is not
;; available in emacs.
(defalias 'backup-file
;; Same feature as "cp -f --backup=numbered from to"
;; Symlinks are copied as file from source unlike
;; `dired-copy-file' which is same as cp -d.
;; Directories are omitted.
(lambda (from to ok)
(cond ((file-directory-p from) (ignore))
(t (let ((count 0))
(while (let ((attrs (file-attributes to)))
(and attrs (null (nth 0 attrs))))
(cl-incf count)
(setq to (concat (file-name-sans-versions to)
(format ".~%s~" count)))))
(condition-case err
(copy-file from to ok dired-copy-preserve-time)
(file-date-error
(dired-log "Can't set date on %s:\n%s\n" from err)))))))
;; Now run the FILE-CREATOR function on files.
(cl-loop with fn = (quote ,file-creator)
for (from . dest) in (quote ,async-fn-list)
do (condition-case err
(funcall fn from dest t)
(file-error
(dired-log "%s: %s\n" (car err) (cdr err))
nil)))
(when (get-buffer dired-log-buffer)
(dired-log t)
(with-current-buffer dired-log-buffer
(write-region (point-min) (point-max)
,dired-async-log-file))))
,(dired-async-maybe-kill-ftp))
callback)
'dired-async-process t)
;; Run mode-line notifications while process running.
(dired-async--modeline-mode 1)
(message "%s proceeding asynchronously..." operation))))
(defvar wdired-use-interactive-rename)
(defun dired-async-wdired-do-renames (old-fn &rest args)
;; Perhaps a better fix would be to ask for renaming BEFORE starting
;; OLD-FN when `wdired-use-interactive-rename' is non-nil. For now
;; just bind it to nil to ensure no questions will be asked between
;; each rename.
(let (wdired-use-interactive-rename)
(apply old-fn args)))
;;;###autoload
(define-minor-mode dired-async-mode
"Do dired actions asynchronously."
:lighter dired-async-mode-lighter
:global t
(if dired-async-mode
(progn
(advice-add 'dired-create-files :around #'dired-async--smart-create-files)
(advice-add 'wdired-do-renames :around #'dired-async-wdired-do-renames))
(progn
(advice-remove 'dired-create-files #'dired-async--smart-create-files)
(advice-remove 'wdired-do-renames #'dired-async-wdired-do-renames))))
(defmacro dired-async--with-async-create-files (&rest body)
"Evaluate BODY with dired-create-files set to dired-async-create-files."
(declare (indent 0))
`(cl-letf (((symbol-function 'dired-create-files) #'dired-async-create-files))
,@body))
;;;###autoload
(defun dired-async-do-copy (&optional arg)
"Run dired-do-copy asynchronously."
(interactive "P")
(dired-async--with-async-create-files
(dired-do-copy arg)))
;;;###autoload
(defun dired-async-do-symlink (&optional arg)
"Run dired-do-symlink asynchronously."
(interactive "P")
(dired-async--with-async-create-files
(dired-do-symlink arg)))
;;;###autoload
(defun dired-async-do-hardlink (&optional arg)
"Run dired-do-hardlink asynchronously."
(interactive "P")
(dired-async--with-async-create-files
(dired-do-hardlink arg)))
;;;###autoload
(defun dired-async-do-rename (&optional arg)
"Run dired-do-rename asynchronously."
(interactive "P")
(dired-async--with-async-create-files
(dired-do-rename arg)))
(provide 'dired-async)
;;; dired-async.el ends here

View file

@ -0,0 +1,71 @@
;;; smtpmail-async.el --- Send e-mail with smtpmail.el asynchronously -*- lexical-binding: t -*-
;; Copyright (C) 2012-2022 Free Software Foundation, Inc.
;; Author: John Wiegley <jwiegley@gmail.com>
;; Created: 18 Jun 2012
;; Keywords: email async
;; X-URL: https://github.com/jwiegley/emacs-async
;; 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:
;; Send e-mail with smtpmail.el asynchronously. To use:
;;
;; (require 'smtpmail-async)
;;
;; (setq send-mail-function 'async-smtpmail-send-it
;; message-send-mail-function 'async-smtpmail-send-it)
;;
;; This assumes you already have smtpmail.el working.
;;; Code:
(defgroup smtpmail-async nil
"Send e-mail with smtpmail.el asynchronously"
:group 'smptmail)
(require 'async)
(require 'smtpmail)
(require 'message)
(defvar async-smtpmail-before-send-hook nil
"Hook running in the child emacs in `async-smtpmail-send-it'.
It is called just before calling `smtpmail-send-it'.")
(defun async-smtpmail-send-it ()
(let ((to (message-field-value "To"))
(buf-content (buffer-substring-no-properties
(point-min) (point-max))))
(message "Delivering message to %s..." to)
(async-start
`(lambda ()
(require 'smtpmail)
(with-temp-buffer
(insert ,buf-content)
(set-buffer-multibyte nil)
;; Pass in the variable environment for smtpmail
,(async-inject-variables
"\\`\\(smtpmail\\|async-smtpmail\\|\\(user-\\)?mail\\)-\\|auth-sources\\|epg\\|nsm"
nil "\\`\\(mail-header-format-function\\|smtpmail-address-buffer\\|mail-mode-abbrev-table\\)")
(run-hooks 'async-smtpmail-before-send-hook)
(smtpmail-send-it)))
(lambda (&optional _ignore)
(message "Delivering message to %s...done" to)))))
(provide 'smtpmail-async)
;;; smtpmail-async.el ends here

View file

@ -0,0 +1,102 @@
;;; bind-key-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "bind-key" "bind-key.el" (0 0 0 0))
;;; Generated autoloads from bind-key.el
(autoload 'bind-key "bind-key" "\
Bind KEY-NAME to COMMAND in KEYMAP (`global-map' if not passed).
KEY-NAME may be a vector, in which case it is passed straight to
`define-key'. Or it may be a string to be interpreted as
spelled-out keystrokes, e.g., \"C-c C-z\". See the documentation
of `edmacro-mode' for details.
COMMAND must be an interactive function, lambda form, or a cons
`(STRING . DEFN)'.
KEYMAP, if present, should be a keymap variable or symbol.
For example:
(bind-key \"M-h\" #\\='some-interactive-function my-mode-map)
(bind-key \"M-h\" #\\='some-interactive-function \\='my-mode-map)
If PREDICATE is non-nil, it is a form evaluated to determine when
a key should be bound. It must return non-nil in such cases.
Emacs can evaluate this form at any time that it does redisplay
or operates on menu data structures, so you should write it so it
can safely be called at any time.
\(fn KEY-NAME COMMAND &optional KEYMAP PREDICATE)" nil t)
(autoload 'unbind-key "bind-key" "\
Unbind the given KEY-NAME, within the KEYMAP (if specified).
See `bind-key' for more details.
\(fn KEY-NAME &optional KEYMAP)" nil t)
(autoload 'bind-key* "bind-key" "\
Similar to `bind-key', but overrides any mode-specific bindings.
\(fn KEY-NAME COMMAND &optional PREDICATE)" nil t)
(autoload 'bind-keys "bind-key" "\
Bind multiple keys at once.
Accepts keyword arguments:
:map MAP - a keymap into which the keybindings should be
added
:prefix KEY - prefix key for these bindings
:prefix-map MAP - name of the prefix map that should be created
for these bindings
:prefix-docstring STR - docstring for the prefix-map variable
:menu-name NAME - optional menu string for prefix map
:repeat-docstring STR - docstring for the repeat-map variable
:repeat-map MAP - name of the repeat map that should be created
for these bindings. If specified, the
`repeat-map' property of each command bound
(within the scope of the `:repeat-map' keyword)
is set to this map.
:exit BINDINGS - Within the scope of `:repeat-map' will bind the
key in the repeat map, but will not set the
`repeat-map' property of the bound command.
:continue BINDINGS - Within the scope of `:repeat-map' forces the
same behaviour as if no special keyword had
been used (that is, the command is bound, and
it's `repeat-map' property set)
:filter FORM - optional form to determine when bindings apply
The rest of the arguments are conses of keybinding string and a
function symbol (unquoted).
\(fn &rest ARGS)" nil t)
(autoload 'bind-keys* "bind-key" "\
Bind multiple keys at once, in `override-global-map'.
Accepts the same keyword arguments as `bind-keys' (which see).
This binds keys in such a way that bindings are not overridden by
other modes. See `override-global-mode'.
\(fn &rest ARGS)" nil t)
(autoload 'describe-personal-keybindings "bind-key" "\
Display all the personal keybindings defined by `bind-key'." t nil)
(register-definition-prefixes "bind-key" '("bind-key" "compare-keybindings" "get-binding-description" "override-global-m" "personal-keybindings"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; bind-key-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; Generated package description from bind-key.el -*- no-byte-compile: t -*-
(define-package "bind-key" "20230203.2004" "A simple way to manage personal keybindings" '((emacs "24.3")) :commit "77945e002f11440eae72d8730d3de218163d551e" :authors '(("John Wiegley" . "johnw@newartisans.com")) :maintainer '("John Wiegley" . "johnw@newartisans.com") :keywords '("keys" "keybinding" "config" "dotemacs" "extensions") :url "https://github.com/jwiegley/use-package")

View file

@ -0,0 +1,567 @@
;;; bind-key.el --- A simple way to manage personal keybindings -*- lexical-binding: t; -*-
;; Copyright (c) 2012-2022 Free Software Foundation, Inc.
;; Author: John Wiegley <johnw@newartisans.com>
;; Maintainer: John Wiegley <johnw@newartisans.com>
;; Created: 16 Jun 2012
;; Version: 2.4.1
;; Package-Version: 20230203.2004
;; Package-Commit: 77945e002f11440eae72d8730d3de218163d551e
;; Package-Requires: ((emacs "24.3"))
;; Keywords: keys keybinding config dotemacs extensions
;; URL: https://github.com/jwiegley/use-package
;; This file is part of GNU Emacs.
;; GNU Emacs 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.
;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; If you have lots of keybindings set in your init file, it can be
;; hard to know which ones you haven't set yet, and which may now be
;; overriding some new default in a new Emacs version. This module
;; aims to solve that problem.
;;
;; Bind keys as follows in your init file:
;;
;; (bind-key "C-c x" 'my-ctrl-c-x-command)
;;
;; If the keybinding argument is a vector, it is passed straight to
;; `define-key', so remapping a key with `[remap COMMAND]' works as
;; expected:
;;
;; (bind-key [remap original-ctrl-c-x-command] 'my-ctrl-c-x-command)
;;
;; If you want the keybinding to override all minor modes that may also bind
;; the same key, use the `bind-key*' form:
;;
;; (bind-key* "<C-return>" 'other-window)
;;
;; If you want to rebind a key only in a particular keymap, use:
;;
;; (bind-key "C-c x" 'my-ctrl-c-x-command some-other-mode-map)
;;
;; To unbind a key within a keymap (for example, to stop your favorite major
;; mode from changing a binding that you don't want to override everywhere),
;; use `unbind-key':
;;
;; (unbind-key "C-c x" some-other-mode-map)
;;
;; To bind multiple keys at once, or set up a prefix map, a `bind-keys' macro
;; is provided. It accepts keyword arguments, please see its documentation
;; for a detailed description.
;;
;; To add keys into a specific map, use :map argument
;;
;; (bind-keys :map dired-mode-map
;; ("o" . dired-omit-mode)
;; ("a" . some-custom-dired-function))
;;
;; To set up a prefix map, use `:prefix-map' and `:prefix' arguments (both are
;; required)
;;
;; (bind-keys :prefix-map my-customize-prefix-map
;; :prefix "C-c c"
;; ("f" . customize-face)
;; ("v" . customize-variable))
;;
;; You can combine all the keywords together. Additionally,
;; `:prefix-docstring' can be specified to set documentation of created
;; `:prefix-map' variable.
;;
;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings
;; will not be overridden by other modes), you may use `bind-keys*' macro:
;;
;; (bind-keys*
;; ("C-o" . other-window)
;; ("C-M-n" . forward-page)
;; ("C-M-p" . backward-page))
;;
;; After Emacs loads, you can see a summary of all your personal keybindings
;; currently in effect with this command:
;;
;; M-x describe-personal-keybindings
;;
;; This display will tell you if you've overridden a default keybinding, and
;; what the default was. Also, it will tell you if the key was rebound after
;; your binding it with `bind-key', and what it was rebound it to.
;;
;; See the `use-package' info manual for more information.
;;; Code:
(require 'cl-lib)
(require 'easy-mmode)
(defgroup bind-key nil
"A simple way to manage personal keybindings."
:group 'keyboard
:group 'convenience
:link '(emacs-commentary-link :tag "Commentary" "bind-key.el")
:version "29.1")
(defcustom bind-key-column-widths '(18 . 40)
"Width of columns in `describe-personal-keybindings'."
:type '(cons integer integer)
:group 'bind-key)
(defcustom bind-key-segregation-regexp
"\\`\\(\\(C-[chx] \\|M-[gso] \\)\\([CM]-\\)?\\|.+-\\)"
"Regexp used by \\[describe-personal-keybindings] to divide key sets."
:type 'regexp
:group 'bind-key)
(defcustom bind-key-describe-special-forms nil
"If non-nil, extract docstrings from lambdas, closures and keymaps if possible."
:type 'boolean
:group 'bind-key)
;; Create override-global-mode to force key remappings
(defvar override-global-map (make-keymap)
"Keymap for `override-global-mode'.")
(define-minor-mode override-global-mode
"A minor mode for allowing keybindings to override other modes.
The main purpose of this mode is to simplify bindings keys in
such a way that they take precedence over other modes.
To achieve this, the keymap `override-global-map' is added to
`emulation-mode-map-alists', which makes it take precedence over
keymaps in `minor-mode-map-alist'. Thereby, key bindings get an
even higher precedence than global key bindings defined with
`keymap-global-set' (or, in Emacs 28 or older, `global-set-key').
The macro `bind-key*' (which see) provides a convenient way to
add keys to that keymap."
:init-value t
:lighter "")
;; the keymaps in `emulation-mode-map-alists' take precedence over
;; `minor-mode-map-alist'
(add-to-list 'emulation-mode-map-alists
`((override-global-mode . ,override-global-map)))
(defvar personal-keybindings nil
"List of bindings performed by `bind-key'.
Elements have the form ((KEY . [MAP]) CMD ORIGINAL-CMD)")
;;;###autoload
(defmacro bind-key (key-name command &optional keymap predicate)
"Bind KEY-NAME to COMMAND in KEYMAP (`global-map' if not passed).
KEY-NAME may be a vector, in which case it is passed straight to
`define-key'. Or it may be a string to be interpreted as
spelled-out keystrokes, e.g., \"C-c C-z\". See the documentation
of `edmacro-mode' for details.
COMMAND must be an interactive function, lambda form, or a cons
`(STRING . DEFN)'.
KEYMAP, if present, should be a keymap variable or symbol.
For example:
(bind-key \"M-h\" #\\='some-interactive-function my-mode-map)
(bind-key \"M-h\" #\\='some-interactive-function \\='my-mode-map)
If PREDICATE is non-nil, it is a form evaluated to determine when
a key should be bound. It must return non-nil in such cases.
Emacs can evaluate this form at any time that it does redisplay
or operates on menu data structures, so you should write it so it
can safely be called at any time."
(let ((namevar (make-symbol "name"))
(keyvar (make-symbol "key"))
(kmapvar (make-symbol "kmap"))
(kdescvar (make-symbol "kdesc"))
(bindingvar (make-symbol "binding")))
`(let* ((,namevar ,key-name)
(,keyvar ,(if (stringp key-name) (read-kbd-macro key-name)
`(if (vectorp ,namevar) ,namevar
(read-kbd-macro ,namevar))))
(,kmapvar (or (if (and ,keymap (symbolp ,keymap))
(symbol-value ,keymap) ,keymap)
global-map))
(,kdescvar (cons (if (stringp ,namevar) ,namevar
(key-description ,namevar))
(if (symbolp ,keymap) ,keymap (quote ,keymap))))
(,bindingvar (lookup-key ,kmapvar ,keyvar)))
(let ((entry (assoc ,kdescvar personal-keybindings))
(details (list ,command
(unless (numberp ,bindingvar)
,bindingvar))))
(if entry
(setcdr entry details)
(add-to-list 'personal-keybindings (cons ,kdescvar details))))
,(if predicate
`(define-key ,kmapvar ,keyvar
'(menu-item "" nil :filter (lambda (&optional _)
(when ,predicate
,command))))
`(define-key ,kmapvar ,keyvar ,command)))))
;;;###autoload
(defmacro unbind-key (key-name &optional keymap)
"Unbind the given KEY-NAME, within the KEYMAP (if specified).
See `bind-key' for more details."
(let ((namevar (make-symbol "name"))
(kdescvar (make-symbol "kdesc")))
`(let* ((,namevar ,key-name)
(,kdescvar (cons (if (stringp ,namevar) ,namevar
(key-description ,namevar))
(if (symbolp ,keymap) ,keymap (quote ,keymap)))))
(bind-key--remove (if (vectorp ,namevar) ,namevar
(read-kbd-macro ,namevar))
(or (if (and ,keymap (symbolp ,keymap))
(symbol-value ,keymap) ,keymap)
global-map))
(setq personal-keybindings
(cl-delete-if (lambda (k) (equal (car k) ,kdescvar))
personal-keybindings))
nil)))
(defun bind-key--remove (key keymap)
"Remove KEY from KEYMAP.
In contrast to `define-key', this function removes the binding from the keymap."
(define-key keymap key nil)
;; Split M-key in ESC key
(setq key (cl-mapcan (lambda (k)
(if (and (integerp k) (/= (logand k ?\M-\0) 0))
(list ?\e (logxor k ?\M-\0))
(list k)))
key))
;; Delete single keys directly
(if (= (length key) 1)
(delete key keymap)
;; Lookup submap and delete key from there
(let* ((prefix (vconcat (butlast key)))
(submap (lookup-key keymap prefix)))
(unless (keymapp submap)
(error "Not a keymap for %s" key))
(when (symbolp submap)
(setq submap (symbol-function submap)))
(delete (last key) submap)
;; Delete submap if it is empty
(when (= 1 (length submap))
(bind-key--remove prefix keymap)))))
;;;###autoload
(defmacro bind-key* (key-name command &optional predicate)
"Similar to `bind-key', but overrides any mode-specific bindings."
`(bind-key ,key-name ,command override-global-map ,predicate))
(defun bind-keys-form (args keymap)
"Bind multiple keys at once.
Accepts keyword arguments:
:map MAP - a keymap into which the keybindings should be
added
:prefix KEY - prefix key for these bindings
:prefix-map MAP - name of the prefix map that should be created
for these bindings
:prefix-docstring STR - docstring for the prefix-map variable
:menu-name NAME - optional menu string for prefix map
:repeat-docstring STR - docstring for the repeat-map variable
:repeat-map MAP - name of the repeat map that should be created
for these bindings. If specified, the
`repeat-map' property of each command bound
(within the scope of the `:repeat-map' keyword)
is set to this map.
:exit BINDINGS - Within the scope of `:repeat-map' will bind the
key in the repeat map, but will not set the
`repeat-map' property of the bound command.
:continue BINDINGS - Within the scope of `:repeat-map' forces the
same behaviour as if no special keyword had
been used (that is, the command is bound, and
it's `repeat-map' property set)
:filter FORM - optional form to determine when bindings apply
The rest of the arguments are conses of keybinding string and a
function symbol (unquoted)."
(let (map
prefix-doc
prefix-map
prefix
repeat-map
repeat-doc
repeat-type ;; Only used internally
filter
menu-name
pkg)
;; Process any initial keyword arguments
(let ((cont t)
(arg-change-func 'cddr))
(while (and cont args)
(if (cond ((and (eq :map (car args))
(not prefix-map))
(setq map (cadr args)))
((eq :prefix-docstring (car args))
(setq prefix-doc (cadr args)))
((and (eq :prefix-map (car args))
(not (memq map '(global-map
override-global-map))))
(setq prefix-map (cadr args)))
((eq :repeat-docstring (car args))
(setq repeat-doc (cadr args)))
((and (eq :repeat-map (car args))
(not (memq map '(global-map
override-global-map))))
(setq repeat-map (cadr args))
(setq map repeat-map))
((eq :continue (car args))
(setq repeat-type :continue
arg-change-func 'cdr))
((eq :exit (car args))
(setq repeat-type :exit
arg-change-func 'cdr))
((eq :prefix (car args))
(setq prefix (cadr args)))
((eq :filter (car args))
(setq filter (cadr args)) t)
((eq :menu-name (car args))
(setq menu-name (cadr args)))
((eq :package (car args))
(setq pkg (cadr args))))
(setq args (funcall arg-change-func args))
(setq cont nil))))
(when (or (and prefix-map (not prefix))
(and prefix (not prefix-map)))
(error "Both :prefix-map and :prefix must be supplied"))
(when repeat-type
(unless repeat-map
(error ":continue and :exit require specifying :repeat-map")))
(when (and menu-name (not prefix))
(error "If :menu-name is supplied, :prefix must be too"))
(unless map (setq map keymap))
;; Process key binding arguments
(let (first next)
(while args
(if (keywordp (car args))
(progn
(setq next args)
(setq args nil))
(if first
(nconc first (list (car args)))
(setq first (list (car args))))
(setq args (cdr args))))
(cl-flet
((wrap (map bindings)
(if (and map pkg (not (memq map '(global-map
override-global-map))))
`((if (boundp ',map)
,(macroexp-progn bindings)
(eval-after-load
,(if (symbolp pkg) `',pkg pkg)
',(macroexp-progn bindings))))
bindings)))
(append
(when prefix-map
`((defvar ,prefix-map)
,@(when prefix-doc `((put ',prefix-map 'variable-documentation ,prefix-doc)))
,@(if menu-name
`((define-prefix-command ',prefix-map nil ,menu-name))
`((define-prefix-command ',prefix-map)))
,@(if (and map (not (eq map 'global-map)))
(wrap map `((bind-key ,prefix ',prefix-map ,map ,filter)))
`((bind-key ,prefix ',prefix-map nil ,filter)))))
(when repeat-map
`((defvar ,repeat-map (make-sparse-keymap)
,@(when repeat-doc `(,repeat-doc)))))
(wrap map
(cl-mapcan
(lambda (form)
(let ((fun (and (cdr form) (list 'function (cdr form)))))
(if prefix-map
`((bind-key ,(car form) ,fun ,prefix-map ,filter))
(if (and map (not (eq map 'global-map)))
;; Only needed in this branch, since when
;; repeat-map is non-nil, map is always
;; non-nil
`(,@(when (and repeat-map (not (eq repeat-type :exit)))
`((put ,fun 'repeat-map ',repeat-map)))
(bind-key ,(car form) ,fun ,map ,filter))
`((bind-key ,(car form) ,fun nil ,filter))))))
first))
(when next
(bind-keys-form `(,@(when repeat-map `(:repeat-map ,repeat-map))
,@(if pkg
(cons :package (cons pkg next))
next)) map)))))))
;;;###autoload
(defmacro bind-keys (&rest args)
"Bind multiple keys at once.
Accepts keyword arguments:
:map MAP - a keymap into which the keybindings should be
added
:prefix KEY - prefix key for these bindings
:prefix-map MAP - name of the prefix map that should be created
for these bindings
:prefix-docstring STR - docstring for the prefix-map variable
:menu-name NAME - optional menu string for prefix map
:repeat-docstring STR - docstring for the repeat-map variable
:repeat-map MAP - name of the repeat map that should be created
for these bindings. If specified, the
`repeat-map' property of each command bound
(within the scope of the `:repeat-map' keyword)
is set to this map.
:exit BINDINGS - Within the scope of `:repeat-map' will bind the
key in the repeat map, but will not set the
`repeat-map' property of the bound command.
:continue BINDINGS - Within the scope of `:repeat-map' forces the
same behaviour as if no special keyword had
been used (that is, the command is bound, and
it's `repeat-map' property set)
:filter FORM - optional form to determine when bindings apply
The rest of the arguments are conses of keybinding string and a
function symbol (unquoted)."
(macroexp-progn (bind-keys-form args nil)))
;;;###autoload
(defmacro bind-keys* (&rest args)
"Bind multiple keys at once, in `override-global-map'.
Accepts the same keyword arguments as `bind-keys' (which see).
This binds keys in such a way that bindings are not overridden by
other modes. See `override-global-mode'."
(macroexp-progn (bind-keys-form args 'override-global-map)))
(defun get-binding-description (elem)
(cond
((listp elem)
(cond
((memq (car elem) '(lambda function))
(if (and bind-key-describe-special-forms
(stringp (nth 2 elem)))
(nth 2 elem)
"#<lambda>"))
((eq 'closure (car elem))
(if (and bind-key-describe-special-forms
(stringp (nth 3 elem)))
(nth 3 elem)
"#<closure>"))
((eq 'keymap (car elem))
"#<keymap>")
(t
elem)))
;; must be a symbol, non-symbol keymap case covered above
((and bind-key-describe-special-forms (keymapp elem))
(let ((doc (get elem 'variable-documentation)))
(if (stringp doc) doc elem)))
((symbolp elem)
elem)
(t
"#<byte-compiled lambda>")))
(defun compare-keybindings (l r)
(let* ((regex bind-key-segregation-regexp)
(lgroup (and (string-match regex (caar l))
(match-string 0 (caar l))))
(rgroup (and (string-match regex (caar r))
(match-string 0 (caar r))))
(lkeymap (cdar l))
(rkeymap (cdar r)))
(cond
((and (null lkeymap) rkeymap)
(cons t t))
((and lkeymap (null rkeymap))
(cons nil t))
((and lkeymap rkeymap
(not (string= (symbol-name lkeymap) (symbol-name rkeymap))))
(cons (string< (symbol-name lkeymap) (symbol-name rkeymap)) t))
((and (null lgroup) rgroup)
(cons t t))
((and lgroup (null rgroup))
(cons nil t))
((and lgroup rgroup)
(if (string= lgroup rgroup)
(cons (string< (caar l) (caar r)) nil)
(cons (string< lgroup rgroup) t)))
(t
(cons (string< (caar l) (caar r)) nil)))))
;;;###autoload
(defun describe-personal-keybindings ()
"Display all the personal keybindings defined by `bind-key'."
(interactive)
(with-output-to-temp-buffer "*Personal Keybindings*"
(princ (format (concat "Key name%s Command%s Comments\n%s %s "
"---------------------\n")
(make-string (- (car bind-key-column-widths) 9) ? )
(make-string (- (cdr bind-key-column-widths) 8) ? )
(make-string (1- (car bind-key-column-widths)) ?-)
(make-string (1- (cdr bind-key-column-widths)) ?-)))
(let (last-binding)
(dolist (binding
(setq personal-keybindings
(sort personal-keybindings
(lambda (l r)
(car (compare-keybindings l r))))))
(if (not (eq (cdar last-binding) (cdar binding)))
(princ (format "\n\n%s: %s\n%s\n\n"
(cdar binding) (caar binding)
(make-string (+ 21 (car bind-key-column-widths)
(cdr bind-key-column-widths)) ?-)))
(if (and last-binding
(cdr (compare-keybindings last-binding binding)))
(princ "\n")))
(let* ((key-name (caar binding))
(at-present (lookup-key (or (symbol-value (cdar binding))
(current-global-map))
(read-kbd-macro key-name)))
(command (nth 1 binding))
(was-command (nth 2 binding))
(command-desc (get-binding-description command))
(was-command-desc (and was-command
(get-binding-description was-command)))
(at-present-desc (get-binding-description at-present)))
(let ((line
(format
(format "%%-%ds%%-%ds%%s\n" (car bind-key-column-widths)
(cdr bind-key-column-widths))
key-name (format "`%s\'" command-desc)
(if (string= command-desc at-present-desc)
(if (or (null was-command)
(string= command-desc was-command-desc))
""
(format "was `%s\'" was-command-desc))
(format "[now: `%s\']" at-present)))))
(princ (if (string-match "[ \t]+\n" line)
(replace-match "\n" t t line)
line))))
(setq last-binding binding)))))
(provide 'bind-key)
;; Local Variables:
;; outline-regexp: ";;;\\(;* [^\s\t\n]\\|###autoload\\)\\|("
;; End:
;;; bind-key.el ends here

View file

@ -0,0 +1,128 @@
;;; burnt-toast-alert.el --- BurntToast integration with alert package -*- lexical-binding: t; coding: utf-8 -*-
;; Copyright (C) 2020 Sam Cedarbaum
;; Author: Sam Cedarbaum (scedarbaum@gmail.com)
;; 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:
;; BurntToast integration with alert package.
;;; Code:
(require 'burnt-toast)
(require 'alert)
(defconst burnt-toast--icon-path-fallback
(concat (file-name-directory load-file-name) "icons/emacs.png")
"Path to fallback icon if one isn't specified.")
(defcustom burnt-toast-icon-path nil
"Path to icon to use for notifications."
:type 'string
:group 'burnt-toast)
(defcustom burnt-toast-alert-enable-remover nil
"Non-nil if alert should remove notifications, nil otherwise."
:type 'boolean
:group 'burnt-toast)
(defcustom burnt-toast-emacs-app-id nil
"The system's AppId for Emacs. Must be an exact match or notifications will fail."
:type 'string
:group 'burnt-toast)
(defcustom burnt-toast-audio-source 'default
"The audio source to play for non-silent notifications."
:type 'symbol
;; TODO: This doesn't actually do anything. Should be a radio-button selection.
:options '(default im mail reminder sms alarm alarm2 alarm3 alarm4 alarm5
alarm6 alarm7 alarm8 alarm9 alarm10 call call2 call3 call4
call5 call6 call7 call8 call9 call10)
:group 'burnt-toast)
(defconst burnt-toast--audio-source-map '((default . "ms-winsoundevent:Notification.Default")
(im . "ms-winsoundevent:Notification.IM")
(mail . "ms-winsoundevent:Notification.Mail")
(reminder . "ms-winsoundevent:Notification.Reminder")
(sms . "ms-winsoundevent:Notification.SMS")
(alarm . "ms-winsoundevent:Notification.Looping.Alarm")
(alarm2 . "ms-winsoundevent:Notification.Looping.Alarm2")
(alarm3 . "ms-winsoundevent:Notification.Looping.Alarm3")
(alarm4 . "ms-winsoundevent:Notification.Looping.Alarm4")
(alarm5 . "ms-winsoundevent:Notification.Looping.Alarm5")
(alarm6 . "ms-winsoundevent:Notification.Looping.Alarm6")
(alarm7 . "ms-winsoundevent:Notification.Looping.Alarm7")
(alarm8 . "ms-winsoundevent:Notification.Looping.Alarm8")
(alarm9 . "ms-winsoundevent:Notification.Looping.Alarm9")
(alarm10 . "ms-winsoundevent:Notification.Looping.Alarm10")
(call . "ms-winsoundevent:Notification.Looping.Call")
(call2 . "ms-winsoundevent:Notification.Looping.Call2")
(call3 . "ms-winsoundevent:Notification.Looping.Call3")
(call4 . "ms-winsoundevent:Notification.Looping.Call4")
(call5 . "ms-winsoundevent:Notification.Looping.Call5")
(call6 . "ms-winsoundevent:Notification.Looping.Call6")
(call7 . "ms-winsoundevent:Notification.Looping.Call7")
(call8 . "ms-winsoundevent:Notification.Looping.Call8")
(call9 . "ms-winsoundevent:Notification.Looping.Call9")
(call10 . "ms-winsoundevent:Notification.Looping.Call10"))
"Mapping from symbols to full audio source names.")
(alert-define-style 'burnt-toast :title "Burnt Toast"
:notifier
(lambda (info)
(let*
;; The message text is :message
((message (plist-get info :message))
;; The :title of the alert
(title (plist-get info :title))
;; The :category of the alert
;; (category (plist-get info :category))
;; The major-mode this alert relates to
;; (mode (plist-get info :mode))
;; The buffer the alert relates to
;; (buffer (plist-get info :buffer))
;; Severity of the alert. It is one of:
;; `urgent'
;; `high'
;; `moderate'
;; `normal'
;; `low'
;; `trivial'
;; (severity (plist-get info :severity))
;; Data which was passed to `alert'. Can be
;; anything.
;; (data (plist-get info :data))
;; Whether this alert should persist, or fade away
;; (persistent (plist-get info :persistent))
(id (plist-get info :id)))
(let* ((title-obj (burnt-toast-bt-text-object :content title))
(message-obj (burnt-toast-bt-text-object :content message))
(icon-path (or burnt-toast-icon-path burnt-toast--icon-path-fallback))
(image (burnt-toast-bt-image-object :source icon-path :app-logo-override t))
(binding (burnt-toast-bt-binding-object :children `(,title-obj ,message-obj) :app-logo-override image))
(visual (burnt-toast-bt-visual-object binding))
(audio-source (cdr (assoc burnt-toast-audio-source burnt-toast--audio-source-map)))
(audio (burnt-toast-bt-audio-object audio-source))
(content (burnt-toast-bt-content-object visual :audio audio)))
(burnt-toast-submit-notification content :unique-identifier id :app-id burnt-toast-emacs-app-id))
:remover
(lambda (info)
(when-let ((id (plist-get info :id)))
(and burnt-toast-alert-enable-remover (burnt-toast-remove-notification :group id)))))))
(provide 'burnt-toast-alert)
;;; burnt-toast-alert.el ends here

View file

@ -0,0 +1,211 @@
;;; burnt-toast-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "burnt-toast" "burnt-toast.el" (0 0 0 0))
;;; Generated autoloads from burnt-toast.el
(autoload 'burnt-toast-submit-notification "burnt-toast" "\
Submit a new notification.
CONTENT is the notification's content.
Should be created with (burnt-toast-bt-content-object ...).
APP-ID is an the application identifier of Emacs on Windows.
UNIQUE-IDENTIFIER will be assigned to the tag and group of the notification.
\(fn CONTENT &key APP-ID UNIQUE-IDENTIFIER)" nil nil)
(autoload 'burnt-toast-bt-header-object "burnt-toast" "\
Create a new header for a notification.
ID is an identifier for the notification. It is used to correlate
the notification with others.
TITLE is the display name for the notification.
\(fn &key ID TITLE)" nil nil)
(autoload 'burnt-toast-bt-text-object "burnt-toast" "\
Create a new text object.
CONTENT is the text content.
MAX-LINES is the maximum number of lines in the text object.
\(fn &key CONTENT MAX-LINES)" nil nil)
(autoload 'burnt-toast-bt-image-object "burnt-toast" "\
Create a new image object.
SOURCE is where the image is located.
APP-LOGO-OVERRIDE is non-nil if image will be used as application icon, nil otherwise.
\(fn &key SOURCE APP-LOGO-OVERRIDE)" nil nil)
(autoload 'burnt-toast-bt-binding-object "burnt-toast" "\
Create a new binding object.
CHILDREN is the elements contained in the binding.
APP-LOGO-OVERRIDE is the image to be used as the app logo.
\(fn &key CHILDREN APP-LOGO-OVERRIDE)" nil nil)
(autoload 'burnt-toast-bt-visual-object "burnt-toast" "\
Create a new visual object.
BINDING-GENERIC is the binding associated with the visual.
\(fn BINDING-GENERIC)" nil nil)
(autoload 'burnt-toast-bt-content-object "burnt-toast" "\
Create a new content object.
VISUAL is the visual associated with the content.
AUDIO is an optional audio object to play.
\(fn VISUAL &key AUDIO)" nil nil)
(autoload 'burnt-toast-bt-audio-object "burnt-toast" "\
Create a new audio object.
SOURCE is the audio's source.
\(fn SOURCE)" nil nil)
(autoload 'burnt-toast-datetime-seconds-from-now "burnt-toast" "\
Return the DateTime SECONDS from now.
\(fn SECONDS)" nil nil)
(autoload 'burnt-toast-new-notification-with-sound "burnt-toast" "\
Create a new notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
SOUND is the sound effect to play.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS).
\(fn &key TEXT APP-LOGO SOUND HEADER UNIQUE-IDENTIFIER EXPIRATION-TIME)" nil nil)
(autoload 'burnt-toast-new-notification-silent "burnt-toast" "\
Create a new silent notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS).
\(fn &key TEXT APP-LOGO HEADER UNIQUE-IDENTIFIER EXPIRATION-TIME)" nil nil)
(autoload 'burnt-toast-new-notification-snooze-and-dismiss-with-sound "burnt-toast" "\
Create a new snooze-and-dismiss notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
SOUND is the sound effect to play.
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS).
\(fn &key TEXT APP-LOGO HEADER SOUND UNIQUE-IDENTIFIER EXPIRATION-TIME)" nil nil)
(autoload 'burnt-toast-new-notification-snooze-and-dismiss-silent "burnt-toast" "\
Create a new silent snooze-and-dismiss notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit
the notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS).
\(fn &key TEXT APP-LOGO HEADER UNIQUE-IDENTIFIER EXPIRATION-TIME)" nil nil)
(autoload 'burnt-toast-new-shoulder-tap "burnt-toast" "\
Create a new shoulder tap notification.
IMAGE is the image representing the contact.
PERSON is the name of the contact.
TEXT is the content of the fallback notification. This can be a list of
strings, in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the fallback notification.
HEADER is the fallback notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS).
\(fn IMAGE PERSON &key TEXT APP-LOGO HEADER EXPIRATION-TIME)" nil nil)
(register-definition-prefixes "burnt-toast" '("burnt-toast-"))
;;;***
;;;### (autoloads nil "burnt-toast-alert" "burnt-toast-alert.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from burnt-toast-alert.el
(register-definition-prefixes "burnt-toast-alert" '("burnt-toast-"))
;;;***
;;;### (autoloads nil nil ("burnt-toast-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; burnt-toast-autoloads.el ends here

View file

@ -0,0 +1,14 @@
(define-package "burnt-toast" "20201113.814" "Elisp integration with the BurntToast PowerShell module"
'((emacs "25.1")
(dash "2.10")
(alert "1.2"))
:commit "e9cf41928b7b502fdfa43718c35a24e503db32e2" :authors
'(("Sam Cedarbaum" . "scedarbaum@gmail.com"))
:maintainer
'("Sam Cedarbaum" . "scedarbaum@gmail.com")
:keywords
'("alert" "notifications" "powershell" "comm")
:url "https://github.com/cedarbaum/burnt-toast.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,389 @@
;;; burnt-toast.el --- Elisp integration with the BurntToast PowerShell module -*- lexical-binding: t; coding: utf-8 -*-
;; Copyright (C) 2020 Sam Cedarbaum
;; Author: Sam Cedarbaum (scedarbaum@gmail.com)
;; Keywords: alert notifications powershell comm
;; Homepage: https://github.com/cedarbaum/burnt-toast.el
;; Version: 0.1
;; Package-Requires: ((emacs "25.1") (dash "2.10") (alert "1.2"))
;; 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:
;; Elisp integration with BurntToast, a PowerShell module for displaying Windows 10 and Windows Server 2019 Toast Notifications.
;;; Code:
(require 'dash)
(require 'cl-lib)
(defcustom burnt-toast-powershell-command "powershell"
"Command to invoke PowerShell."
:type 'string
:group 'burnt-toast)
(defvar burnt-toast--verbose nil "Enable verbose logging.")
(defvar burnt-toast-powershell-test-hook nil "Hook to intercept powershell command for testing.")
(defvar burnt-toast--install-checked nil "Cache if installation has already been checked.")
(defun burnt-toast--check-installation ()
"Check if PowerShell and BurntToast module are installed and on PATH."
(unless burnt-toast--install-checked
(unless (executable-find burnt-toast-powershell-command)
(error "PowerShell executable not on PATH"))
(unless (eq 0 (burnt-toast--run-powershell-command "Get-Command New-BurntToastNotification" t))
(error "BurntToast module cannot be found"))
(setq burnt-toast--install-checked t)))
;; Based on: https://github.com/mplscorwin/erc-burnt-toast-blob/master/erc-burnt-toast.el
(defun burnt-toast--sanitize-powershell-input (string)
"Return a version of STRING sanitized for use as input to PowerShell.
New-lines are removed, trailing spaces are removed, and single-quotes are doubled."
(when (stringp string)
(replace-regexp-in-string
"\s+$" ""
(replace-regexp-in-string
"[\t\n\r]+" ""
(replace-regexp-in-string
"\"" "\"\""
string)))))
(defun burnt-toast--quote-and-sanitize-string (string)
"Surround STRING with double quotes and sanitize it when it is non-nil."
(when string
(concat "\"" (burnt-toast--sanitize-powershell-input string) "\"")))
(defun burnt-toast--param-to-string (obj)
"Return OBJ as string when a non-nil string or an empty string otherwise."
(cond ((stringp obj) obj)
((numberp obj) (number-to-string obj))
(t "")))
(defun burnt-toast--run-powershell-command (command-and-args &optional skip-install-check)
"Execute a PowerShell command COMMAND-AND-ARGS.
Optionally skip BurntToast installation check with SKIP-INSTALL-CHECK."
(when burnt-toast--verbose (message command-and-args))
(if burnt-toast-powershell-test-hook
(apply burnt-toast-powershell-test-hook `(,command-and-args))
(or skip-install-check (burnt-toast--check-installation))
(call-process burnt-toast-powershell-command nil nil nil
"-NoProfile" "-NoExit" "-NonInteractive" "-WindowStyle" "Hidden" command-and-args)))
(defun burnt-toast--create-ps-command (command-prefix args)
"Create a new PowerShell command with prefix COMMAND-PREFIX using ARGS."
(let* ((prefix-string (concat "$(" command-prefix " "))
(non-nil-args (-filter (-lambda ((_ value)) value) args))
(quoted-args (-map
(-lambda ((arg value quote)) `(,arg ,(if quote (burnt-toast--quote-and-sanitize-string value) value)))
non-nil-args))
(args-string-list (-map
(-lambda ((arg value)) (concat "-" arg " " (burnt-toast--param-to-string value)))
quoted-args))
(args-string (and args-string-list (-reduce (lambda (s1 s2) (concat s1 " " s2)) args-string-list))))
(concat prefix-string (or args-string "") ")")))
(defun burnt-toast--new-ps-object (object args)
"Create a new PowerShell OBJECT with ARGS."
(let* ((command-prefix (concat "New-" object)))
(burnt-toast--create-ps-command command-prefix args)))
(defun burnt-toast--new-ps-object-list (objects &optional process)
"Create a comma separated list of OBJECTS.
Optionally process each object with PROCESS function as list is built."
(let* ((map-func (or process #'identity)))
(if (and objects (listp objects))
(-reduce
(lambda (s1 s2) (concat s1 "," s2))
(-map map-func objects))
(apply map-func `(,objects)))))
(cl-defun burnt-toast--new-notification-core (&key text app-logo sound header silent snooze-and-dismiss
unique-identifier expiration-time)
"Create new notification with subset of arguments.
Arguments are TEXT, APP-LOGO, SOUND, HEADER, SILENT, SNOOZE-AND-DISMISS,
UNIQUE-IDENTIFIER, and EXPIRATION-TIME.
This function should not be called directly."
(let* ((processed-text (burnt-toast--new-ps-object-list text #'burnt-toast--quote-and-sanitize-string))
(ps-command (burnt-toast--new-ps-object
"BurntToastNotification"
`(("Text" ,processed-text)
("AppLogo" ,app-logo t)
("Sound" ,sound t)
("Header" ,header)
("Silent" ,silent)
("SnoozeAndDismiss" ,snooze-and-dismiss)
("UniqueIdentifier" ,unique-identifier)
("ExpirationTime" ,expiration-time)))))
(burnt-toast--run-powershell-command ps-command)))
;;;###autoload
(cl-defun burnt-toast-submit-notification (content &key app-id
unique-identifier)
"Submit a new notification.
CONTENT is the notification's content.
Should be created with (burnt-toast-bt-content-object ...).
APP-ID is an the application identifier of Emacs on Windows.
UNIQUE-IDENTIFIER will be assigned to the tag and group of the notification."
(let* ((ps-command (burnt-toast--create-ps-command
"Submit-BTNotification"
`(("Content" ,content)
("AppId" ,app-id t)
("UniqueIdentifier" ,unique-identifier t)))))
(burnt-toast--run-powershell-command ps-command)))
;;;###autoload
(cl-defun burnt-toast-bt-header-object (&key id title)
"Create a new header for a notification.
ID is an identifier for the notification. It is used to correlate
the notification with others.
TITLE is the display name for the notification."
(burnt-toast--new-ps-object
"BTHeader"
`(("Id" ,id)
("Title" ,title t))))
;;;###autoload
(cl-defun burnt-toast-bt-text-object (&key content max-lines)
"Create a new text object.
CONTENT is the text content.
MAX-LINES is the maximum number of lines in the text object."
(burnt-toast--new-ps-object
"BTText"
`(("Content" ,content t)
("MaxLines" ,max-lines))))
;;;###autoload
(cl-defun burnt-toast-bt-image-object (&key source app-logo-override)
"Create a new image object.
SOURCE is where the image is located.
APP-LOGO-OVERRIDE is non-nil if image will be used as application icon, nil otherwise."
(burnt-toast--new-ps-object
"BTImage"
`(("Source" ,source t)
("AppLogoOverride" ,app-logo-override))))
;;;###autoload
(cl-defun burnt-toast-bt-binding-object (&key children app-logo-override)
"Create a new binding object.
CHILDREN is the elements contained in the binding.
APP-LOGO-OVERRIDE is the image to be used as the app logo."
(burnt-toast--new-ps-object
"BTBinding"
`(("Children" ,(burnt-toast--new-ps-object-list children))
("AppLogoOverride" ,app-logo-override))))
;;;###autoload
(cl-defun burnt-toast-bt-visual-object (binding-generic)
"Create a new visual object.
BINDING-GENERIC is the binding associated with the visual."
(burnt-toast--new-ps-object
"BTVisual"
`(("BindingGeneric" ,binding-generic))))
;;;###autoload
(cl-defun burnt-toast-bt-content-object (visual &key audio)
"Create a new content object.
VISUAL is the visual associated with the content.
AUDIO is an optional audio object to play."
(burnt-toast--new-ps-object
"BTContent"
`(("Visual" ,visual)
("Audio" ,audio))))
;;;###autoload
(cl-defun burnt-toast-bt-audio-object (source)
"Create a new audio object.
SOURCE is the audio's source."
(burnt-toast--new-ps-object
"BTAudio"
`(("Source" ,source))))
;;;###autoload
(cl-defun burnt-toast-datetime-seconds-from-now (seconds)
"Return the DateTime SECONDS from now."
(format "$([DateTime]::Now.AddSeconds(%f))" seconds))
;;;###autoload
(cl-defun burnt-toast-new-notification-with-sound (&key text app-logo sound header unique-identifier
expiration-time)
"Create a new notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
SOUND is the sound effect to play.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS)."
(burnt-toast--new-notification-core
:text text
:app-logo app-logo
:sound sound
:header header
:unique-identifier unique-identifier
:expiration-time expiration-time))
;;;###autoload
(cl-defun burnt-toast-new-notification-silent (&key text app-logo header unique-identifier
expiration-time)
"Create a new silent notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS)."
(burnt-toast--new-notification-core
:text text
:app-logo app-logo
:silent t
:header header
:unique-identifier unique-identifier
:expiration-time expiration-time))
;;;###autoload
(cl-defun burnt-toast-new-notification-snooze-and-dismiss-with-sound (&key text app-logo header sound unique-identifier
expiration-time)
"Create a new snooze-and-dismiss notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
SOUND is the sound effect to play.
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit the
notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS)."
(burnt-toast--new-notification-core
:text text
:app-logo app-logo
:header header
:sound sound
:snooze-and-dismiss t
:unique-identifier unique-identifier
:expiration-time expiration-time))
;;;###autoload
(cl-defun burnt-toast-new-notification-snooze-and-dismiss-silent (&key text app-logo header unique-identifier
expiration-time)
"Create a new silent snooze-and-dismiss notification.
TEXT is the content of the notification. This can be a list of strings,
in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the notification.
HEADER is the notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
UNIQUE-IDENTIFIER a unique identifier that can be used to remove/edit
the notification.
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS)."
(burnt-toast--new-notification-core
:text text
:app-logo app-logo
:silent t
:snooze-and-dismiss t
:header header
:unique-identifier unique-identifier
:expiration-time expiration-time))
;;;###autoload
(cl-defun burnt-toast-new-shoulder-tap (image person &key text app-logo header expiration-time)
"Create a new shoulder tap notification.
IMAGE is the image representing the contact.
PERSON is the name of the contact.
TEXT is the content of the fallback notification. This can be a list of
strings, in which case each entry is a new line.
APP-LOGO is a path to an icon to be displayed with the fallback notification.
HEADER is the fallback notification's header.
This should be created with (burnt-toast-bt-header-object ID HEADER).
EXPIRATION-TIME DateTime for notification to expire.
This should be created with (burnt-toast-datetime-seconds-from-now SECONDS)."
(let* ((processed-text (burnt-toast--new-ps-object-list text #'burnt-toast--quote-and-sanitize-string))
(ps-command (burnt-toast--new-ps-object
"BurntToastShoulderTap"
`(("Image" ,image t)
("Person" ,person t)
("Text" ,processed-text)
("AppLogo" ,app-logo t)
("Header" ,header)
("ExpirationTime" ,expiration-time)))))
(burnt-toast--run-powershell-command ps-command)))
(cl-defun burnt-toast-remove-notification (&key app-id tag group)
"Remove a notification.
If APP-ID is specified, removes all notifications for that application.
If TAG is specified, removes all notifications with that tag.
If GROUP is specified, removes all notifications in that group."
(let* ((ps-command (burnt-toast--create-ps-command
"Remove-BTNotification"
`(("AppId" ,app-id t)
("Tag" ,tag t)
("Group" ,group)))))
(burnt-toast--run-powershell-command ps-command)))
(provide 'burnt-toast)
;;; burnt-toast.el ends here

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,103 @@
;;; centaur-tabs-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "centaur-tabs" "centaur-tabs.el" (0 0 0 0))
;;; Generated autoloads from centaur-tabs.el
(autoload 'centaur-tabs-local-mode "centaur-tabs" "\
Toggle local display of the tab bar.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
When turned on, if a local header line is shown, it is hidden to show
the tab bar. The tab bar is locally hidden otherwise. When turned
off, if a local header line is hidden or the tab bar is locally
hidden, it is shown again. Signal an error if Centaur-Tabs mode is off.
\(fn &optional ARG)" t nil)
(defvar centaur-tabs-mode nil "\
Non-nil if Centaur-Tabs mode is enabled.
See the `centaur-tabs-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `centaur-tabs-mode'.")
(custom-autoload 'centaur-tabs-mode "centaur-tabs" nil)
(autoload 'centaur-tabs-mode "centaur-tabs" "\
Toggle display of a tab bar in the header line.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
\\{centaur-tabs-mode-map}
\(fn &optional ARG)" t nil)
(register-definition-prefixes "centaur-tabs" '("centaur-tabs-"))
;;;***
;;;### (autoloads nil "centaur-tabs-elements" "centaur-tabs-elements.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-elements.el
(register-definition-prefixes "centaur-tabs-elements" '("cent"))
;;;***
;;;### (autoloads nil "centaur-tabs-functions" "centaur-tabs-functions.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-functions.el
(autoload 'centaur-tabs-backward "centaur-tabs-functions" "\
Select the previous available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t nil)
(autoload 'centaur-tabs-forward "centaur-tabs-functions" "\
Select the next available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t nil)
(autoload 'centaur-tabs-backward-group "centaur-tabs-functions" "\
Go to selected tab in the previous available group." t nil)
(autoload 'centaur-tabs-forward-group "centaur-tabs-functions" "\
Go to selected tab in the next available group." t nil)
(autoload 'centaur-tabs-backward-tab "centaur-tabs-functions" "\
Select the previous visible tab." t nil)
(autoload 'centaur-tabs-forward-tab "centaur-tabs-functions" "\
Select the next visible tab." t nil)
(register-definition-prefixes "centaur-tabs-functions" '("centaur-tabs-"))
;;;***
;;;### (autoloads nil "centaur-tabs-interactive" "centaur-tabs-interactive.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-interactive.el
(autoload 'centaur-tabs-counsel-switch-group "centaur-tabs-interactive" "\
Display a list of current buffer groups using Counsel." t nil)
(register-definition-prefixes "centaur-tabs-interactive" '("centaur-tabs-"))
;;;***
;;;### (autoloads nil nil ("centaur-tabs-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; centaur-tabs-autoloads.el ends here

View file

@ -0,0 +1,865 @@
;;; centaur-tabs-elements.el --- centaur-tabs visual components and customizations -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2020 Emmanuel Bustos
;; Package-Requires: ((emacs "24.4") (powerline "2.4") (cl-lib "0.5"))
;; This file is not part of GNU Emacs.
;;
;; 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 2, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;; This file contains the visual components of centaur-tabs
;;; Code:
;;
;;; Requires
;;
(require 'color)
(require 'powerline)
;;; Faces
;;
(defface centaur-tabs-default
'((t
(:background "black" :foreground "black")))
"Default face used in the tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-unselected
'((t
(:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected tab."
:group 'centaur-tabs)
(defface centaur-tabs-unselected-modified
'((t
(:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected-modified tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected-modified
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected-modified tab."
:group 'centaur-tabs)
(defface centaur-tabs-close-unselected
'((t
(:inherit centaur-tabs-unselected)))
"Face used for unselected close button."
:group 'centaur-tabs)
(defface centaur-tabs-close-selected
'((t (:inherit centaur-tabs-selected)))
"Face used for selected close button."
:group 'centaur-tabs)
(defface centaur-tabs-name-mouse-face
'((t nil))
"Face used for tab name when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-close-mouse-face
'((t (:inherit underline)))
"Face used for close button when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-selected
`((t (:inherit centaur-tabs-selected)))
"Face used for selected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-unselected
`((t (:inherit centaur-tabs-unselected)))
"Face used for unselected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-active-bar-face
'((t (:background "cyan")))
"Face used for selected tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-selected
'((t (:inherit centaur-tabs-modified-marker-selected :weight extra-bold)))
"Face used for selected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-unselected
'((t (:inherit centaur-tabs-modified-marker-unselected :weight extra-bold)))
"Face used for unselected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-dim-buffer-face
'((t (:foreground "gray40")))
"Face for the buffer when centaur-tabs-ace-jump is invoked.")
;;; Tabs' display line
;;
(defvar centaur-tabs-display-line
(if (boundp 'tab-line-format)
'tab-line
'header-line))
(defvar centaur-tabs-display-line-format
(if (boundp 'tab-line-format)
'tab-line-format
'header-line-format))
;;; Tabs' characteristics
;;
(defcustom centaur-tabs-style "bar"
"The style of tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-label-fixed-length 0
"Fixed length of label. Set to 0 if dynamic."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-background-color
(face-background 'centaur-tabs-default nil 'default)
"*Background color of the tab bar.
By default, use the background color specified for the
`centaur-tabs-default' face (or inherited from another face), or the
background color of the `default' face otherwise."
:group 'centaur-tabs
:type 'face)
(defcustom centaur-tabs-height 22
"The height of tab."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-bar-height (+ 8 centaur-tabs-height)
"The height of bar."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-mouse-pointer 'hand
"Cursor to display when hovering the tabs.
Default is 'hand. The following scopes are possible:
- arrow
- hand
- vdrag
- hdrag
- modeline
- hourglass"
:group 'centaur-tabs
:type 'variable)
;;; Icons
;;
(defcustom centaur-tabs-set-icons nil
"When non nil, display an icon based on `centaur-tabs-icon-type' alongside the tab name."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icon-type (and centaur-tabs-set-icons
(or (require 'all-the-icons nil t)
(require 'nerd-icons nil t)))
"Icon type. It should be one of `all-the-icons' and `nerd-icons'."
:group 'centaur-tabs
:type 'symbol
:set
(lambda (k v)
(pcase v
('all-the-icons
(unless (require 'all-the-icons nil t)
(setq v nil)))
('nerd-icons
(unless (require 'nerd-icons nil t)
(setq v nil)))
(type
(if (require 'all-the-icons nil t)
(setq v 'all-the-icons)
(setq v nil))))
(set k v)))
(defvar centaur-tabs-icon-scale-factor
1.0
"The base scale factor for the `height' face property of tab icons.")
(defvar centaur-tabs-icon-v-adjust
0.01
"The vertical adjust for tab icons.")
(defcustom centaur-tabs-gray-out-icons nil
"When non nil, enable gray icons for unselected buffer."
:group 'centaur-tabs
:type '(choice :tag "Gray out icons for unselected..."
(const :tag "Buffer" buffer)))
(defcustom centaur-tabs-plain-icons nil
"When non nil, tab icons' color will be the same as tabs' foreground color."
:group 'centaur-tabs
:type 'boolean)
(defun centaur-tabs--icon-for-file (file &rest args)
"Get the formatted icon for FILE.
ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-file file args))
('nerd-icons (apply #'nerd-icons-icon-for-file file args))))
(defun centaur-tabs--icon-for-mode (mode &rest args)
"Get the formatted icon for MODE.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-mode mode args))
('nerd-icons (apply #'nerd-icons-icon-for-mode mode args))))
(defun centaur-tabs--auto-mode-match? (&optional file)
"Whether or not FILE's `major-mode' match against its `auto-mode-alist'."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-auto-mode-match? file))
('nerd-icons (apply #'nerd-icons-auto-mode-match? file))))
(defun centaur-tabs-icon (tab face selected)
"Generate icon for TAB using FACE's background.
If icon gray out option enabled, gray out icon if not SELECTED."
(if centaur-tabs-icon-type
(with-current-buffer (car tab)
(let* ((icon
(if (and (buffer-file-name)
(centaur-tabs--auto-mode-match?))
(centaur-tabs--icon-for-file
(file-name-nondirectory (buffer-file-name))
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor)
(centaur-tabs--icon-for-mode
major-mode
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor)))
(background (face-background face nil 'default))
(inactive (cond ((and (not selected)
(eq centaur-tabs-gray-out-icons 'buffer))
(face-foreground 'mode-line-inactive nil 'default))
(centaur-tabs-plain-icons
(face-foreground 'centaur-tabs-selected nil 'default))
(t 'unspecified)))
(underline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'under)
(face-attribute face :underline)))
(overline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'over)
(face-attribute face :overline))))
(if (stringp icon)
(progn
(propertize icon 'face `(:inherit ,(get-text-property 0 'face icon)
:foreground ,inactive
:background ,background
:underline ,underline
:overline ,overline)))
"")))
""))
;;; Ace-window style tab switching
;;
(defcustom centaur-tabs-show-jump-identifier 'prompted
"Whether to show the tab identifier for centaur-tabs-ace-jump.
It has 3 options:
- 'nil, never show the jump identifier.
- 'prompted, only show it when using centaur-tabs-ace-jump.
- 'always, always show it regardless of the status."
:group 'centaur-tabs
:type '(choice :tag "show identifier when..."
(const :tag "Never" nil)
(const :tag "Only when prompted" prompted)
(const :tag "Always" always)))
(defcustom centaur-tabs-ace-jump-dim-buffer t
"Whether to dim the current buffer when centaur-ace-jump is activated.")
(defvar centaur-tabs-ace-jump-keys
'(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)
"Buffer jump keys used by centaur-tabs-ace-jump.")
(defvar centuar-tabs-ace-dispatch-alist
'((?q exit "Exit")
(?\C-g exit "Exit")
(?j jump-to-tab "Jump to tab")
(?x close-tab "Close tab")
(?s swap-tab "Swap tab")
(?\[ backward-group "Previous group")
(?\] forward-group "Next group")
(?? show-help "Show dispatch help"))
"Action keys used by centaur-tabs-ace-jump.
The value of each element must be in the form:
\(key keyword docstring), where keyword must be one of the follows:
\(exit, jump-to-tab, close-tab, swap-tab, backward-group,
forward-group, show-help).")
;;; Close buttons, modified marker and edges' margins
;;
(defcustom centaur-tabs-set-close-button t
"When non nil, display a clickable close button on the right side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-set-left-close-button nil
"When non nil, display a clickable close button on the left side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-close-button (make-string 1 #x00D7)
"Display appearance of the close buttons, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-set-modified-marker nil
"When non nil, display a marker when the buffer is modified."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-modified-marker (make-string 1 #x23FA)
"Display appearance of the modified marker, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-left-edge-margin " "
"Text to display at the left edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-right-edge-margin " "
"Text to display at the right edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
;;; Selected tab bar
;;
(defcustom centaur-tabs-set-bar nil
"When non nil, display a bar to show the currently selected tab.
There are three options:
- 'left: displays the bar at the left of the currently selected tab.
- 'under: displays the bar under the currently selected tab.
- 'over: displays the bar over the currently selected tab."
:group 'centaur-tabs
:type '(choice :tag "Display bar at..."
(const :tag "Put bar on the left" left)
(const :tag "Put bar as an underline" under)
(const :tag "Put bar as an overline" over)))
(defun centaur-tabs--make-xpm (face width height)
"Create an XPM bitmap via FACE WIDTH and HEIGHT.
Taken from `doom-modeline'."
(when (and (display-graphic-p)
(image-type-available-p 'xpm))
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or (face-background face nil t) "None")))
(ignore-errors
(create-image
(concat
(format
"/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
(length (car data)) (length data) color color)
(apply #'concat
(cl-loop with idx = 0
with len = (length data)
for dl in data
do (cl-incf idx)
collect
(concat
"\""
(cl-loop for d in dl
if (= d 0) collect (string-to-char " ")
else collect (string-to-char "."))
(if (eq idx len) "\"};" "\",\n")))))
'xpm t :ascent 'center))))))
(defvar centaur-tabs-active-bar
(centaur-tabs--make-xpm 'centaur-tabs-active-bar-face
2
centaur-tabs-bar-height))
;;; Navigation buttons
;;
(defcustom centaur-tabs-show-navigation-buttons nil
"When non-nil, show the buttons for backward/forward tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-down-tab-text ""
"Text icon to show in the down button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-backward-tab-text ""
"Text icon to show in the backward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-forward-tab-text ""
"Text icon to show in the forward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-show-count nil
"When non-nil, show the current index and count of tabs in the current group."
:group 'centaur-tabs
:type 'boolean)
;;; New tab button
;;
(defcustom centaur-tabs-show-new-tab-button t
"When non-nil, show the button to create a new tab."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-new-tab-text " + "
"Text icon to show in the new-tab button."
:group 'centaur-tabs
:type 'string)
;;; Separators
;;
(defvar centaur-tabs-style-left nil)
(defvar centaur-tabs-style-right nil)
(defvar ns-use-srgb-colorspace)
(defvar centaur-tabs-image-apple-rgb
(and (eq (window-system) 'ns)
ns-use-srgb-colorspace
(< 11
(string-to-number
(and (string-match "darwin\\([0-9]+\\)" system-configuration)
(match-string-no-properties 1 system-configuration)))))
"Boolean variable to determine whether to use Apple RGB colorspace.
used to render images.
t on macOS 10.7+ and `ns-use-srgb-colorspace' is t, nil otherwise.
This variable is automatically set, there's no need to modify it.")
(defun centaur-tabs-separator-interpolate (color1 color2)
"Interpolate between COLOR1 and COLOR2.
COLOR1 and COLOR2 must be supplied as hex strings with a leading #."
(let* ((c1 (color-name-to-rgb color1))
(c2 (color-name-to-rgb color2))
(red (/ (+ (nth 0 c1) (nth 0 c2)) 2))
(green (/ (+ (nth 1 c1) (nth 1 c2)) 2))
(blue (/ (+ (nth 2 c1) (nth 2 c2)) 2)))
(color-rgb-to-hex red green blue)))
(defun centaur-tabs-separator-color-xyz-to-apple-rgb (X Y Z)
"Convert CIE X Y Z colors to Apple RGB color space."
(let ((r (+ (* 3.2404542 X) (* -1.5371385 Y) (* -0.4985314 Z)))
(g (+ (* -0.9692660 X) (* 1.8760108 Y) (* 0.0415560 Z)))
(b (+ (* 0.0556434 X) (* -0.2040259 Y) (* 1.0572252 Z))))
(list (expt r (/ 1.8)) (expt g (/ 1.8)) (expt b (/ 1.8)))))
(defun centaur-tabs-separator-color-srgb-to-apple-rgb (red green blue)
"Convert RED GREEN BLUE colors from sRGB color space to Apple RGB.
RED, GREEN and BLUE should be between 0.0 and 1.0, inclusive."
(apply #'centaur-tabs-separator-color-xyz-to-apple-rgb (color-srgb-to-xyz red green blue)))
(defun centaur-tabs-separator-hex-color (color)
"Get the hexadecimal value of COLOR."
(when color
(let ((srgb-color (color-name-to-rgb color)))
(if centaur-tabs-image-apple-rgb
(apply #'color-rgb-to-hex (apply #'centaur-tabs-separator-color-srgb-to-apple-rgb srgb-color))
(apply #'color-rgb-to-hex srgb-color)))))
(defun centaur-tabs-separator-pattern (lst)
"Turn LST into an infinite pattern."
(when lst
(let ((pattern (cl-copy-list lst)))
(setcdr (last pattern) pattern))))
(defun centaur-tabs-separator-pattern-to-string (pattern)
"Convert a PATTERN into a string that can be used in an XPM."
(concat "\"" (mapconcat #'number-to-string pattern "") "\","))
(defun centaur-tabs-separator-reverse-pattern (pattern)
"Reverse each line in PATTERN."
(cl-mapcar 'reverse pattern))
(defun centaur-tabs-separator-row-pattern (fill total &optional fade)
"Make a list that has FILL 0s out of TOTAL 1s with FADE 2s to the right of the fill."
(unless fade
(setq fade 0))
(let ((fill (min fill total))
(fade (min fade (max (- total fill) 0))))
(append (make-list fill 0)
(make-list fade 2)
(make-list (- total fill fade) 1))))
(defun centaur-tabs-separator-pattern-bindings-body (patterns height-exp pattern-height-sym
second-pattern-height-sym)
"Create let-var bindings and a function body from PATTERNS.
The `car' and `cdr' parts of the result can be passed to the
function `centaur-tabs-separator-wrap-defun' as its `let-vars'
and `body' arguments,respectively. HEIGHT-EXP is an expression
calculating the image height and it should contain a free variable `height'.
PATTERN-HEIGHT-SYM and SECOND-PATTERN-HEIGHT-SYM are symbols used
for let-var binding variables."
(let* ((pattern (centaur-tabs-separator-pattern (cl-mapcar 'centaur-tabs-separator-pattern-to-string (car patterns))))
(header (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 1 patterns)))
(footer (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 2 patterns)))
(second-pattern (centaur-tabs-separator-pattern (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 3 patterns))))
(center (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 4 patterns)))
(reserve (+ (length header) (length footer) (length center))))
(when pattern
(cons `((,pattern-height-sym (max (- ,height-exp ,reserve) 0))
(,second-pattern-height-sym (/ ,pattern-height-sym 2))
(,pattern-height-sym ,(if second-pattern `(ceiling ,pattern-height-sym 2) `,pattern-height-sym)))
(list (when header `(mapconcat 'identity ',header ""))
`(mapconcat 'identity
(cl-subseq ',pattern 0 ,pattern-height-sym) "")
(when center `(mapconcat 'identity ',center ""))
(when second-pattern
`(mapconcat 'identity
(cl-subseq ',second-pattern
0 ,second-pattern-height-sym) ""))
(when footer `(mapconcat 'identity ',footer "")))))))
(defun centaur-tabs-separator-pattern-defun (name dir width &rest patterns)
"Create a powerline function of NAME in DIR with WIDTH for PATTERNS.
PATTERNS is of the form (PATTERN HEADER FOOTER SECOND-PATTERN CENTER
PATTERN-2X HEADER-2X FOOTER-2X SECOND-PATTERN-2X CENTER-2X).
PATTERN is required, all other components are optional.
The first 5 components are for the standard resolution image.
The remaining ones are for the high resolution image where both
width and height are doubled. If PATTERN-2X is nil or not given,
then the remaining components are ignored and the standard
resolution image with magnification and interpolation will be
used in high resolution environments
All generated functions generate the form:
HEADER
PATTERN ...
CENTER
SECOND-PATTERN ...
FOOTER
PATTERN and SECOND-PATTERN repeat infinitely to fill the space needed to
generate a full height XPM.
PATTERN, HEADER, FOOTER, SECOND-PATTERN, CENTER are of the form
\((COLOR ...) (COLOR ...) ...).
COLOR can be one of 0, 1, or 2, where 0 is the source color, 1 is the
destination color, and 2 is the interpolated color between 0 and 1."
(when (eq dir 'right)
(setq patterns (cl-mapcar 'centaur-tabs-separator-reverse-pattern patterns)))
(let ((bindings-body (centaur-tabs-separator-pattern-bindings-body patterns
'height
'pattern-height
'second-pattern-height))
(bindings-body-2x (centaur-tabs-separator-pattern-bindings-body (nthcdr 5 patterns)
'(* height 2)
'pattern-height-2x
'second-pattern-height-2x)))
(centaur-tabs-separator-wrap-defun name dir width
(append (car bindings-body) (car bindings-body-2x))
(cdr bindings-body) (cdr bindings-body-2x))))
(defun centaur-tabs-separator-background-color (face)
"Set the separator background color using FACE."
(face-attribute face
(if (face-attribute face :inverse-video nil 'default)
:foreground
:background)
nil
'default))
(defun centaur-tabs-separator-wrap-defun (name dir width let-vars body &optional body-2x)
"Generate a powerline function of name NAME in dir DIR.
This is made with WIDTH using LET-VARS and BODY.
BODY-2X is an optional argument."
(let* ((src-face (if (eq dir 'left) 'face1 'face2))
(dst-face (if (eq dir 'left) 'face2 'face1)))
`(defun ,(intern (format "powerline-%s-%s" name (symbol-name dir)))
(face1 face2 &optional height)
(when window-system
(unless height (setq height centaur-tabs-height))
(let* ,(append `((color1 (when ,src-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,src-face))))
(color2 (when ,dst-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,dst-face))))
(colori (when (and color1 color2) (centaur-tabs-separator-interpolate color1 color2)))
(color1 (or color1 "None"))
(color2 (or color2 "None"))
(colori (or colori "None")))
let-vars)
(apply #'create-image
,(append `(concat (format "/* XPM */ static char * %s_%s[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
,width
height
color1
color2
colori))
body
'("};"))
'xpm t
:ascent 'center
:face (when (and face1 face2)
,dst-face)
,(and body-2x
`(and (featurep 'mac)
(list :data-2x
,(append `(concat (format "/* XPM */ static char * %s_%s_2x[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
(* ,width 2)
(* height 2)
color1
color2
colori))
body-2x
'("};")))))))))))
(defun centaur-tabs-separator-alternate (dir)
"Generate an alternating pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "alternate" dir 4
'((2 2 1 1)
(0 0 2 2))
nil nil nil nil
;; 2x
'((2 2 2 2 1 1 1 1)
(2 2 2 2 1 1 1 1)
(0 0 0 0 2 2 2 2)
(0 0 0 0 2 2 2 2))))
(defun centaur-tabs-separator-bar (dir)
"Generate a bar XPM function for DIR."
(centaur-tabs-separator-pattern-defun "bar" dir 2
'((2 2))))
(defun centaur-tabs-separator-box (dir)
"Generate a box XPM function for DIR."
(centaur-tabs-separator-pattern-defun "box" dir 2
'((0 0)
(0 0)
(1 1)
(1 1))
nil nil nil nil
;; 2x
'((0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1))))
(defun centaur-tabs-separator-chamfer (dir)
"Generate a chamfer XPM function for DIR."
(centaur-tabs-separator-pattern-defun "chamfer" dir 3
'((0 0 0))
'((1 1 1)
(0 1 1)
(0 0 1))
nil nil nil
;; 2x
'((0 0 0 0 0 0))
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1))))
(defun centaur-tabs-separator-rounded (dir)
"Generate a rounded XPM function for DIR."
(centaur-tabs-separator-pattern-defun "rounded" dir 6
'((0 0 0 0 0 0))
'((2 1 1 1 1 1)
(0 0 2 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 2 1)
(0 0 0 0 0 1)
(0 0 0 0 0 2))
nil nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0))
'((1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 2 1 1 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 2 1)
(0 0 0 0 0 0 0 0 0 0 0 1)
(0 0 0 0 0 0 0 0 0 0 0 1))))
(defun centaur-tabs-separator-slant (dir)
"Generate a slant XPM function for DIR."
(let* ((row-modifier (if (eq dir 'left) 'identity 'reverse)))
(centaur-tabs-separator-wrap-defun "slant" dir 'width
'((width (1- (ceiling height 2))))
`((cl-loop for i from 0 to (1- height)
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) width)))))
`((cl-loop for i from 0 to (1- (* height 2))
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) (* width 2)))))))))
(defun centaur-tabs-separator-wave (dir)
"Generate a wave XPM function for DIR."
(centaur-tabs-separator-pattern-defun "wave" dir 11
'((0 0 0 0 0 0 1 1 1 1 1))
'((2 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1))
'((0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 2 1 1)
(0 0 0 0 0 0 0 0 0 0 2))
nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1))
'((1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1))
'((0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))))
(defun centaur-tabs-separator-zigzag (dir)
"Generate a zigzag pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "zigzag" dir 3
'((1 1 1)
(0 1 1)
(0 0 1)
(0 0 0)
(0 0 1)
(0 1 1))
nil nil nil nil
;; 2x
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1)
(0 0 0 0 0 0)
(0 0 0 0 0 1)
(0 0 0 0 1 1)
(0 0 0 1 1 1)
(0 0 1 1 1 1)
(0 1 1 1 1 1))))
(defun centaur-tabs-separator-memoize (func)
"Memoize FUNC.
If argument is a symbol then install the memoized function over
the original function. Use frame-local memoization."
(cl-typecase func
(symbol (fset func (centaur-tabs-separator-memoize-wrap-frame-local (symbol-function func))) func)
(function (centaur-tabs-separator-memoize-wrap-frame-local func))))
(defun centaur-tabs-separator-memoize-wrap-frame-local (func)
"Return the memoized version of FUNC.
The memoization cache is frame-local."
(let ((funcid (cl-gensym)))
`(lambda (&rest args)
,(concat (documentation func) (format "\n(memoized function %s)" funcid))
(let* ((cache (centaur-tabs-separator-create-or-get-cache))
(key (cons ',funcid args))
(val (gethash key cache)))
(if val
val
(puthash key (apply ,func args) cache))))))
(defun centaur-tabs-separator-create-or-get-cache ()
"Return a frame-local hash table that acts as a memoization cache.
The cache is for the powerline.
Create one if the frame doesn't have one yet."
(let ((table (frame-parameter nil 'powerline-cache)))
(if (hash-table-p table) table (centaur-tabs-separator-reset-cache))))
(defun centaur-tabs-separator-reset-cache ()
"Reset and return the frame-local hash table used for a memoization cache."
(let ((table (make-hash-table :test 'equal)))
;; Store it as a frame-local variable
(modify-frame-parameters nil `((powerline-cache . ,table)))
table))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'right))
(defun centaur-tabs-select-separator-style (tab-style)
"Set the separator style to TAB-STYLE."
(setq centaur-tabs-style-left (funcall (intern (format "powerline-%s-right" tab-style)) 'centaur-tabs-default nil centaur-tabs-height))
(setq centaur-tabs-style-right (funcall (intern (format "powerline-%s-left" tab-style)) nil 'centaur-tabs-default centaur-tabs-height)))
(provide 'centaur-tabs-elements)
;;; centaur-tabs-elements.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,627 @@
;;; centaur-tabs-interactive.el --- centaur-tabs interactive functions and plugins support lexical-binding: t; -*-
;; Copyright (C) 2019-2020 Emmanuel Bustos
;; This file is not part of GNU Emacs.
;;
;; 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 2, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;; This file contains centaur-tabs interactive functions and plugins support
;;; Code:
;;; Requires
(require 'centaur-tabs-elements)
(require 'centaur-tabs-functions)
;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;
(defun centaur-tabs-switch-group (&optional groupname)
"Switch tab groups using ido. GROUPNAME can optionaly be provided."
(interactive)
(let* ((tab-buffer-list (cl-mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall centaur-tabs-buffer-groups-function) )))
(funcall centaur-tabs-buffer-list-function)))
(groups (centaur-tabs-get-groups))
(group-name (or groupname (centaur-tabs-completing-read "Groups: " groups))) )
(catch 'done
(mapc
#'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun centaur-tabs-select-end-tab ()
"Select end tab of current tabset."
(interactive)
(centaur-tabs-select-beg-tab t))
(defun centaur-tabs-select-beg-tab (&optional backward)
"Select beginning tab of current tabs.
If BACKWARD is non-nil, move backward, otherwise move forward.
TYPE is default option."
(interactive)
(let* ((tabset (centaur-tabs-current-tabset t))
(ttabset (centaur-tabs-get-tabsets-tabset))
(_cycle (if (and (eq centaur-tabs-cycle-scope 'groups)
(not (cdr (centaur-tabs-tabs ttabset))))
'tabs
centaur-tabs-cycle-scope))
_selected tab)
(when tabset
(setq tabset (centaur-tabs-tabs tabset)
tab (car (if backward (last tabset) tabset)))
(centaur-tabs-buffer-select-tab tab))))
(defun centaur-tabs-backward-tab-other-window (&optional reversed)
"Move to left tab in other window.
Optional argument REVERSED default is move backward, if reversed is non-nil move forward."
(interactive)
(other-window 1)
(if reversed
(centaur-tabs-forward-tab)
(centaur-tabs-backward-tab))
(other-window -1))
(defun centaur-tabs-forward-tab-other-window ()
"Move to right tab in other window."
(interactive)
(centaur-tabs-backward-tab-other-window t))
(defun centaur-tabs-move-current-tab-to-right ()
"Move current tab one place right, unless it's already the rightmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(new-bufs (list))
the-buffer)
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push (car old-bufs) new-bufs)
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(setq the-buffer (car old-bufs))
(setq old-bufs (cdr old-bufs))
(if old-bufs ; if this is false, then the current tab is the rightmost
(push (car old-bufs) new-bufs))
(push the-buffer new-bufs)) ; this is the tab that was to be moved
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs)))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update)))
(defun centaur-tabs-move-current-tab-to-left ()
"Move current tab one place left, unless it's already the leftmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(first-buf (car old-bufs))
(new-bufs (list))
not-yet-this-buf)
(if (string= (buffer-name) (format "%s" (car first-buf)))
old-bufs ; the current tab is the leftmost
(setq not-yet-this-buf first-buf)
(setq old-bufs (cdr old-bufs))
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push not-yet-this-buf new-bufs)
(setq not-yet-this-buf (car old-bufs))
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(push (car old-bufs) new-bufs) ; this is the tab that was to be moved
(push not-yet-this-buf new-bufs)
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs))))
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update))))
(defmacro centaur-tabs-kill-buffer-match-rule (match-rule)
"If buffer match MATCH-RULE, kill it."
`(save-excursion
(mapc #'(lambda (buffer)
(with-current-buffer buffer
(when (string-equal current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(when (funcall ,match-rule buffer)
(kill-buffer buffer))
)))
(buffer-list))))
(defun centaur-tabs-kill-all-buffers-in-current-group ()
"Kill all buffers in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t)))))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (_buffer) t))
;; Switch to next group.
(centaur-tabs-forward-group)
))
(defun centaur-tabs-kill-other-buffers-in-current-group ()
"Kill all buffers except current buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (equal buffer currentbuffer))))
))
(defun centaur-tabs-kill-unmodified-buffers-in-current-group ()
"Kill all unmodified buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (buffer-modified-p buffer))))
))
(defun centaur-tabs-kill-match-buffers-in-current-group ()
"Kill all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Kill buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (string-equal (file-name-extension filename) match-extension))
)))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))
))
(defun centaur-tabs-keep-match-buffers-in-current-group ()
"Keep all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Just keep buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (not (string-equal (file-name-extension filename) match-extension)))
)))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))
))
(defun centaur-tabs-select-visible-nth-tab (tab-index)
"Select visible tab with TAB-INDEX'.
Example, when `tab-index' is 1, this function will select the leftmost label in
the visible area, instead of the first label in the current group.
If `tab-index' more than length of visible tabs, selet the last tab.
If `tab-index' is 0, select last tab."
(let ((visible-tabs (centaur-tabs-view centaur-tabs-current-tabset)))
(switch-to-buffer
(car
(if (or (equal tab-index 0)
(> tab-index (length visible-tabs)))
(car (last visible-tabs))
(nth (- tab-index 1) visible-tabs))))))
(defun centaur-tabs-select-visible-tab ()
"Bind this function with number keystroke, such as s-1, s-2, s-3 ... etc.
This function automatically recognizes the number at the end of the keystroke
and switches to the tab of the corresponding index.
Note that this function switches to the visible range,
not the actual logical index position of the current group."
(interactive)
(let* ((event last-command-event)
(key (make-vector 1 event))
(key-desc (key-description key)))
(centaur-tabs-select-visible-nth-tab
(string-to-number (car (last (split-string key-desc "-")))))))
;; ace-jump style tab switching
(defvar centaur-tabs-ace-jump-active nil
"t if centaur-tabs-ace-jump is invoked.")
(defvar centaur-tabs-dim-overlay nil
"Holds the overlay for dimming buffer when invoking centaur-tabs-ace-jump.")
(defun centaur-tabs--dim-window ()
"Create a dim background overlay for the current window."
(when centaur-tabs-ace-jump-dim-buffer
(when centaur-tabs-dim-overlay
(delete-overlay centaur-tabs-dim-overlay))
(setq centaur-tabs-dim-overlay
(let ((ol (make-overlay (window-start) (window-end))))
(overlay-put ol 'face 'centaur-tabs-dim-buffer-face)
ol))))
(defun centaur-tabs-swap-tab (tab)
"Swap the position of current tab with TAB.
TAB has to be in the same group as the current tab."
(if (eq (centaur-tabs-tab-tabset tab) (centaur-tabs-current-tabset t))
(let* ((group (centaur-tabs-current-tabset t))
(tabs (cl-copy-list (centaur-tabs-tabs group)))
(current (centaur-tabs-selected-tab group))
(current-index (cl-position current tabs))
(target-index (cl-position tab tabs)))
(if (eq tab current)
(message "Can't swap with current tab itself.")
(setcar (nthcdr current-index tabs) tab)
(setcar (nthcdr target-index tabs) current)
(set group tabs)
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)))
(message "Error: %s is not in the same group as the current tab." tab)))
(defun centaur-tabs-ace-action (action)
"Preform ACTION on a visible tab. Ace-jump style.
ACTION has to be one of value in `centuar-tabs-ace-dispatch-alist'"
(when (centaur-tabs-current-tabset t)
(when centaur-tabs-ace-jump-dim-buffer
(centaur-tabs--dim-window))
(cond ((eq action 'jump-to-tab)
(message "Jump to tab: "))
((eq action 'close-tab)
(message "Close tab: "))
((eq action 'swap-tab)
(message "Swap current tab with: ")))
(let ((centaur-tabs-ace-jump-active t))
(catch 'done
(while t
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)
(let ((char (read-key)) (action-cache))
(cond
;; tab keys
((memq char centaur-tabs-ace-jump-keys)
(let ((sel (nth (cl-position char centaur-tabs-ace-jump-keys) (centaur-tabs-view (centaur-tabs-current-tabset t)))))
(cond ((eq sel nil)
(message "Tab %s does not exist" (key-description (vector char))))
((eq action 'jump-to-tab)
(centaur-tabs-buffer-select-tab sel))
((eq action 'close-tab)
(centaur-tabs-buffer-close-tab sel))
((eq action 'swap-tab)
(centaur-tabs-swap-tab sel))))
(throw 'done nil))
;; actions
((setq action-cache (assoc char centuar-tabs-ace-dispatch-alist))
(setq action-cache (cadr action-cache))
(cond ((eq action-cache 'exit) ; exit
(message "Quit")
(throw 'done nil))
((eq action-cache 'forward-group) ; forward group
(message "Forward group")
(centaur-tabs-forward-group)
(centaur-tabs--dim-window))
((eq action-cache 'backward-group) ; backward group
(message "Backward group")
(centaur-tabs-backward-group)
(centaur-tabs--dim-window))
((eq action-cache 'show-help) ; help menu
(message "%s" (mapconcat
(lambda (elem) (format "%s: %s"
(key-description (vector (car elem)))
(caddr elem)))
centuar-tabs-ace-dispatch-alist
"\n")))
(t (setq action action-cache) ; other actions
(cond ((eq action-cache 'jump-to-tab)
(message "Jump to tab: "))
((eq action-cache 'close-tab)
(message "Close tab: "))
((eq action-cache 'swap-tab)
(message "Swap current tab with: "))))))
;; no match, repeat
(t
(message "No such candidate: %s, hit ? for help." (key-description (vector char)))))))))
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(when centaur-tabs-ace-jump-dim-buffer
(delete-overlay centaur-tabs-dim-overlay)
(setq centaur-tabs-dim-overlay nil))
(centaur-tabs-display-update)))
(defun centaur-tabs-ace-jump (&optional arg)
"Select a tab and perform an action. Ace-jump style.
If no ARG is provided, select that tab.
If prefixed with one `universal-argument', swap the current
tab with the selected tab.
If prefixed with two `universal-argument's, close
selected tab."
(interactive "p")
(cond ((eq arg 1)
(centaur-tabs-ace-action 'jump-to-tab))
((eq arg 4)
(centaur-tabs-ace-action 'swap-tab))
((eq arg 16)
(centaur-tabs-ace-action 'close-tab))
(t
(centaur-tabs-ace-action 'jump-to-tab))))
(defun centaur-tabs-group-buffer-groups ()
"Use centaur-tabs's own buffer grouping function."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-buffer-groups)
(centaur-tabs-display-update))
;; Projectile integration. Taken from tabbar-ruler
(defvar centaur-tabs-projectile-buffer-group-calc nil
"Set buffer groups for projectile.
Should be buffer local and speed up calculation of buffer groups.")
(defun centaur-tabs-projectile-buffer-groups ()
"Return the list of group names BUFFER belongs to."
(if centaur-tabs-projectile-buffer-group-calc
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)
(set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc)
(cond
((or (get-buffer-process (current-buffer)) (memq major-mode '(comint-mode compilation-mode))) '("Term"))
((string-equal "*" (substring (buffer-name) 0 1)) '("Misc"))
((condition-case _err
(projectile-project-root)
(error nil)) (list (projectile-project-name)))
((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode
c++-mode javascript-mode js-mode
js2-mode makefile-mode
lua-mode vala-mode)) '("Coding"))
((memq major-mode '(nxhtml-mode html-mode
mhtml-mode css-mode)) '("HTML"))
((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org"))
((memq major-mode '(dired-mode)) '("Dir"))
(t '("Other"))))
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)))
(defun centaur-tabs-group-by-projectile-project()
"Group by projectile project."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-projectile-buffer-groups)
(centaur-tabs-display-update))
;; Show groups instead of tabs
(defun centaur-tabs-toggle-groups ()
"Show group names on the tabs instead of buffer names."
(interactive)
(centaur-tabs-buffer-show-groups (not centaur-tabs--buffer-show-groups))
(centaur-tabs-display-update))
;; Helm source for switching group in helm.
(defun centaur-tabs-build-helm-source ()
"Display a list of current buffer groups in Helm."
(interactive)
(setq helm-source-centaur-tabs-group
(when (featurep 'helm)
(require 'helm)
(helm-build-sync-source "Centaur-Tabs Group"
:candidates #'centaur-tabs-get-groups
:action '(("Switch to group" . centaur-tabs-switch-group))))))
;; Ivy source for switching group in ivy.
;;;###autoload
(defun centaur-tabs-counsel-switch-group ()
"Display a list of current buffer groups using Counsel."
(interactive)
(when (featurep 'ivy)
(require 'ivy)
(ivy-read
"Centaur Tabs Groups:"
(centaur-tabs-get-groups)
:action #'centaur-tabs-switch-group
:caller 'centaur-tabs-counsel-switch-group)))
(defun centaur-tabs-extract-window-to-new-frame()
"Kill the current window in the current frame, and open the current buffer in a new frame."
(interactive)
(unless (centaur-tabs--one-window-p)
(let ((buffer (current-buffer)))
(delete-window)
(display-buffer-pop-up-frame buffer nil))))
(defun centaur-tabs--copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
;;; From https://emacsredux.com/blog/2013/03/27/copy-filename-to-the-clipboard/
(interactive)
(let* ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name)))
(filename (expand-file-name filename)))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the kill ring." filename))))
(defun centaur-tabs-open-directory-in-external-application ()
"Open the current directory in a external application."
(interactive)
(centaur-tabs--open-externally default-directory))
(defun centaur-tabs-open-in-external-application ()
"Open the file of the current buffer according to its mime type."
(interactive)
(let ((path (if (buffer-file-name) (buffer-file-name) default-directory)))
(centaur-tabs--open-externally path)))
(defun centaur-tabs--open-externally (file-or-path)
"Open FILE-OR-PATH according to its mime type in an external application.
FILE-OR-PATH is expanded with `expand-file-name`.
Modified copy of `treemacs-visit-node-in-external-application`."
(let ((path (expand-file-name file-or-path)))
(pcase system-type
('windows-nt
(declare-function w32-shell-execute "w32fns.c")
(w32-shell-execute "open" (replace-regexp-in-string "/" "\\" path t t)))
('darwin
(shell-command (format "open \"%s\"" path)))
('gnu/linux
(let ((process-connection-type nil))
(start-process "" nil "xdg-open" path)))
(_ (message "Don't know how to open files on %s." (symbol-name system-type))))))
(defun centaur-tabs--copy-directory-name-to-clipboard ()
"Copy the current directory name to the clipboard."
(interactive)
(when default-directory
(kill-new default-directory)
(message "Copied directory name '%s' to the kill ring." (expand-file-name default-directory))))
(defun centaur-tabs--tab-submenu-groups-definition ()
"Menu definition with a list of tab groups."
(mapcar (lambda (s) `[,s ,s]) (sort (centaur-tabs-get-groups) #'string<)))
(defun centaur-tabs--tab-submenu-tabs-definition ()
"Menu definition with a list of tabs for the current group."
(let* ((tabset (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))
(tabs-in-group (centaur-tabs-tabs tabset))
(buffers (mapcar #'centaur-tabs-tab-value tabs-in-group))
(sorted-tabnames (sort (mapcar #'buffer-name buffers) #'string<)))
(mapcar (lambda (s) `[,s ,s]) sorted-tabnames)))
(defvar centaur-tabs--groups-submenu-key "Tab groups")
(defvar centaur-tabs--tabs-submenu-key "Go to tab of group")
(defun centaur-tabs--kill-this-buffer-dont-ask()
"Kill the current buffer without confirmation."
(interactive)
(kill-buffer (current-buffer))
(centaur-tabs-display-update)
(redisplay t))
(defun centaur-tabs--tab-menu-definition ()
"Definition of the context menu of a tab."
`(["Kill this buffer" centaur-tabs--kill-this-buffer-dont-ask]
["Kill other buffers of group" centaur-tabs-kill-other-buffers-in-current-group]
["Kill unmodified buffers of group" centaur-tabs-kill-unmodified-buffers-in-current-group]
"----"
["Split below" split-window-below]
["Split right" split-window-right]
"----"
["Maximize tab" delete-other-windows
:active (null (centaur-tabs--one-window-p))]
["Extract to new frame" centaur-tabs-extract-window-to-new-frame
:active (null (centaur-tabs--one-window-p))]
["Duplicate in new frame" make-frame-command]
"----"
["Copy filepath" centaur-tabs--copy-file-name-to-clipboard
:active (buffer-file-name)]
["Copy directory path" centaur-tabs--copy-directory-name-to-clipboard
:active default-directory]
["Open in external application" centaur-tabs-open-in-external-application
:active (or (buffer-file-name) default-directory)]
["Open directory in dired" dired-jump
:active (not (eq major-mode 'dired-mode))]
["Open directory externally" centaur-tabs-open-directory-in-external-application
:active default-directory]
"----"
,( append (list centaur-tabs--groups-submenu-key) (centaur-tabs--tab-submenu-groups-definition))
,( append (list centaur-tabs--tabs-submenu-key) (centaur-tabs--tab-submenu-tabs-definition))
))
(defun centaur-tabs--one-window-p ()
"Like `one-window-p`, but taking into account side windows like treemacs."
(let* ((mainwindow (window-main-window))
(child-count (window-child-count mainwindow)))
(= 0 child-count)))
(defun centaur-tabs--get-tab-from-name (tabname)
"Get the tab from the current group given de TABNAME."
(let ((seq (centaur-tabs-tabs (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))))
(cl-find-if
(lambda (tab) (string= tabname (buffer-name (centaur-tabs-tab-value tab))))
seq)))
(defun centaur-tabs--tab-menu (event)
"Show a context menu for the clicked tab or button. The clicked tab, identified by EVENT, is selected."
(interactive "e" )
(let ((click-on-tab-p (ignore-errors (centaur-tabs-get-tab-from-event event))))
(when (not click-on-tab-p)
(centaur-tabs--groups-menu))
(when click-on-tab-p
(centaur-tabs-do-select event)
(redisplay t)
(let*
((menu (easy-menu-create-menu nil (centaur-tabs--tab-menu-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let* ((menu-key (first choice))
(choice-is-group-p (string= centaur-tabs--groups-submenu-key (symbol-name menu-key)))
(name (car (last choice)))
(name-as-string (symbol-name name)))
(if choice-is-group-p
(centaur-tabs-switch-group name-as-string)
(switch-to-buffer name-as-string))))))))
(defun centaur-tabs--groups-menu ()
"Show a popup menu with the centaur tabs groups."
(interactive)
(let*
((sorted-groups (centaur-tabs--tab-submenu-groups-definition))
(menu (easy-menu-create-menu "Tab groups" (centaur-tabs--tab-submenu-groups-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let ((group (car (last choice))))
(centaur-tabs-switch-group (format "%s" group))))))
(provide 'centaur-tabs-interactive)
;;; centaur-tabs-interactive.el ends here

View file

@ -0,0 +1,14 @@
(define-package "centaur-tabs" "20230607.1501" "Aesthetic, modern looking customizable tabs plugin"
'((emacs "24.4")
(powerline "2.4")
(cl-lib "0.5"))
:commit "0bb1aa18d475319df85f192dce3327802866c3c3" :authors
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainers
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainer
'("Emmanuel Bustos" . "ema2159@gmail.com")
:url "https://github.com/ema2159/centaur-tabs")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,218 @@
;;; centaur-tabs.el --- Aesthetic, modern looking customizable tabs plugin -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Emmanuel Bustos
;; Filename: centaur-tabs.el
;; Description: Provide an out of box configuration to use highly customizable tabs.
;; URL: https://github.com/ema2159/centaur-tabs
;; Author: Emmanuel Bustos <ema2159@gmail.com>
;; Maintainer: Emmanuel Bustos <ema2159@gmail.com>
;; Created: 2019-21-19 22:14:34
;; Version: 5
;; Known Compatibility: GNU Emacs 26.2
;; Package-Requires: ((emacs "24.4") (powerline "2.4") (cl-lib "0.5"))
;;
;;
;;; This file is NOT part of GNU Emacs
;;; License
;;
;; 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, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; Emacs plugin aiming to become an aesthetic, modern looking tabs plugin.
;;
;; This package offers tabs with a wide range of customization options, both
;; aesthetical and functional, implementing them trying to follow the Emacs
;; philosophy packing them with useful keybindings and a nice integration
;; with the Emacs environment, without sacrificing customizability.
;; Some of the features Centaur tabs offers are:
;; - Tab styles
;; - Tab icons
;; - Graying out icons
;; - Selected tab bar (over, under and left bar)
;; - Close button
;; - Modified marker
;; - Buffer grouping
;; - Projectile integration
;; - Ivy and Helm integration for group switching
;;
;;; Code:
;;; Requires
(require 'centaur-tabs-elements)
(require 'centaur-tabs-functions)
(require 'centaur-tabs-interactive)
;;;;;;;;;;;;;;;;;;;;;;; Centaur-Tabs source code ;;;;;;;;;;;;;;;;;;;;;;;
(defgroup centaur-tabs nil
"Display a tab bar in the header line."
:group 'convenience)
(defvar centaur-tabs--buffer-show-groups nil)
;;; Minor modes
;;
(defsubst centaur-tabs-mode-on-p ()
"Return non-nil if Centaur-Tabs mode is on."
(eq (default-value centaur-tabs-display-line-format)
centaur-tabs-header-line-format))
;;; Centaur-Tabs-Local mode
;;
(defvar centaur-tabs--local-hlf nil)
;;;###autoload
(define-minor-mode centaur-tabs-local-mode
"Toggle local display of the tab bar.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
When turned on, if a local header line is shown, it is hidden to show
the tab bar. The tab bar is locally hidden otherwise. When turned
off, if a local header line is hidden or the tab bar is locally
hidden, it is shown again. Signal an error if Centaur-Tabs mode is off."
:group 'centaur-tabs
:global nil
(unless (centaur-tabs-mode-on-p)
(error "Centaur-Tabs mode must be enabled"))
;;; ON
(if centaur-tabs-local-mode
(if (and (local-variable-p centaur-tabs-display-line-format)
(eval centaur-tabs-display-line-format))
;; A local header line exists, hide it to show the tab bar.
(progn
;; Fail in case of an inconsistency because another local
;; header line is already hidden.
(when (local-variable-p 'centaur-tabs--local-hlf)
(error "Another local header line is already hidden"))
(set (make-local-variable 'centaur-tabs--local-hlf)
(eval centaur-tabs-display-line-format))
(kill-local-variable centaur-tabs-display-line-format))
;; Otherwise hide the tab bar in this buffer.
(set centaur-tabs-display-line-format nil))
;;; OFF
(if (local-variable-p 'centaur-tabs--local-hlf)
;; A local header line is hidden, show it again.
(progn
(set centaur-tabs-display-line-format centaur-tabs--local-hlf)
(kill-local-variable 'centaur-tabs--local-hlf))
;; The tab bar is locally hidden, show it again.
(kill-local-variable centaur-tabs-display-line-format))))
;;; Centaur-Tabs mode
;;
(defvar centaur-tabs--global-hlf nil)
;;;###autoload
(define-minor-mode centaur-tabs-mode
"Toggle display of a tab bar in the header line.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
\\{centaur-tabs-mode-map}"
:group 'centaur-tabs
:require 'centaur-tabs
:global t
:keymap centaur-tabs-mode-map
(if centaur-tabs-mode
;;; ON
(unless (centaur-tabs-mode-on-p)
;; Save current default value of `centaur-tabs-display-line-format'.
(setq centaur-tabs--global-hlf (default-value centaur-tabs-display-line-format))
(centaur-tabs-init-tabsets-store)
(set-default centaur-tabs-display-line-format centaur-tabs-header-line-format))
;;; OFF
(when (centaur-tabs-mode-on-p)
;; Turn off Centaur-Tabs-Local mode globally.
(mapc #'(lambda (b)
(condition-case nil
(with-current-buffer b
(and centaur-tabs-local-mode
(centaur-tabs-local-mode -1)))
(error nil)))
(buffer-list))
;; Restore previous `centaur-tabs-display-line-format'.
(set-default centaur-tabs-display-line-format centaur-tabs--global-hlf)
(centaur-tabs-free-tabsets-store))
))
;;; Tab bar buffer setup
;;
(defun centaur-tabs-buffer-init ()
"Initialize tab bar buffer data.
Run as `centaur-tabs-init-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function 'centaur-tabs-buffer-tabs
centaur-tabs-tab-label-function 'centaur-tabs-buffer-tab-label
centaur-tabs-select-tab-function 'centaur-tabs-buffer-select-tab
)
;; If set, initialize selected overline
(when (eq centaur-tabs-set-bar 'under)
(set-face-attribute 'centaur-tabs-selected nil
:underline (face-background 'centaur-tabs-active-bar-face nil 'default)
:overline nil)
(set-face-attribute 'centaur-tabs-selected-modified nil
:underline (face-background 'centaur-tabs-active-bar-face nil 'default)
:overline nil)
(set-face-attribute 'centaur-tabs-unselected nil
:underline nil
:overline nil)
(set-face-attribute 'centaur-tabs-unselected-modified nil
:underline nil
:overline nil))
(when (eq centaur-tabs-set-bar 'over)
(set-face-attribute 'centaur-tabs-selected nil
:overline (face-background 'centaur-tabs-active-bar-face nil 'default)
:underline nil)
(set-face-attribute 'centaur-tabs-selected-modified nil
:overline (face-background 'centaur-tabs-active-bar-face nil 'default)
:underline nil)
(set-face-attribute 'centaur-tabs-unselected nil
:overline nil
:underline nil)
(set-face-attribute 'centaur-tabs-unselected-modified nil
:overline nil
:underline nil))
(add-hook 'after-save-hook #'centaur-tabs-on-saving-buffer)
(add-hook 'first-change-hook #'centaur-tabs-on-modifying-buffer)
(add-hook 'kill-buffer-hook #'centaur-tabs-buffer-track-killed)
(advice-add #'undo :after #'centaur-tabs-after-modifying-buffer)
(advice-add #'undo-tree-undo-1 :after #'centaur-tabs-after-modifying-buffer)
(advice-add #'undo-tree-redo-1 :after #'centaur-tabs-after-modifying-buffer))
(defun centaur-tabs-buffer-quit ()
"Quit tab bar buffer.
Run as `centaur-tabs-quit-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function nil
centaur-tabs-tab-label-function nil
centaur-tabs-select-tab-function nil
)
(remove-hook 'after-save-hook 'centaur-tabs-after-modifying-buffer)
(remove-hook 'first-change-hook 'centaur-tabs-on-modifying-buffer)
(remove-hook 'kill-buffer-hook 'centaur-tabs-buffer-track-killed)
(advice-remove #'undo #'centaur-tabs-after-modifying-buffer)
(advice-remove #'undo-tree-undo-1 #'centaur-tabs-after-modifying-buffer)
(advice-remove #'undo-tree-redo-1 #'centaur-tabs-after-modifying-buffer))
(add-hook 'centaur-tabs-init-hook #'centaur-tabs-buffer-init)
(add-hook 'centaur-tabs-quit-hook #'centaur-tabs-buffer-quit)
(provide 'centaur-tabs)
;;; centaur-tabs.el ends here

View file

@ -0,0 +1,85 @@
;;; centaur-tabs-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from centaur-tabs.el
(autoload 'centaur-tabs-local-mode "centaur-tabs" "\
Toggle local display of the tab bar.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
When turned on, if a local header line is shown, it is hidden to show
the tab bar. The tab bar is locally hidden otherwise. When turned
off, if a local header line is hidden or the tab bar is locally
hidden, it is shown again. Signal an error if Centaur-Tabs mode is off.
(fn &optional ARG)" t)
(defvar centaur-tabs-mode nil "\
Non-nil if Centaur-Tabs mode is enabled.
See the `centaur-tabs-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `centaur-tabs-mode'.")
(custom-autoload 'centaur-tabs-mode "centaur-tabs" nil)
(autoload 'centaur-tabs-mode "centaur-tabs" "\
Toggle display of a tab bar in the header line.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
\\{centaur-tabs-mode-map}
(fn &optional ARG)" t)
(register-definition-prefixes "centaur-tabs" '("centaur-tabs-"))
;;; Generated autoloads from centaur-tabs-elements.el
(register-definition-prefixes "centaur-tabs-elements" '("centaur-tabs-"))
;;; Generated autoloads from centaur-tabs-functions.el
(autoload 'centaur-tabs-backward "centaur-tabs-functions" "\
Select the previous available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t)
(autoload 'centaur-tabs-forward "centaur-tabs-functions" "\
Select the next available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t)
(autoload 'centaur-tabs-backward-group "centaur-tabs-functions" "\
Go to selected tab in the previous available group." t)
(autoload 'centaur-tabs-forward-group "centaur-tabs-functions" "\
Go to selected tab in the next available group." t)
(autoload 'centaur-tabs-backward-tab "centaur-tabs-functions" "\
Select the previous visible tab." t)
(autoload 'centaur-tabs-forward-tab "centaur-tabs-functions" "\
Select the next visible tab." t)
(register-definition-prefixes "centaur-tabs-functions" '("centaur-tabs-"))
;;; Generated autoloads from centaur-tabs-interactive.el
(autoload 'centaur-tabs-counsel-switch-group "centaur-tabs-interactive" "\
Display a list of current buffer groups using Counsel." t)
(register-definition-prefixes "centaur-tabs-interactive" '("centaur-tabs-"))
;;; End of scraped data
(provide 'centaur-tabs-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; centaur-tabs-autoloads.el ends here

View file

@ -0,0 +1,891 @@
;;; centaur-tabs-elements.el --- centaur-tabs visual components and customizations -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; This file is not part of GNU Emacs.
;; 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 2, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; This file contains the visual components of centaur-tabs
;;
;;; Code:
(require 'custom)
(require 'color)
(require 'powerline)
;; Compiler pacifier
(declare-function all-the-icons-icon-for-file "ext:all-the-icons.el" t t)
(declare-function all-the-icons-icon-for-mode "ext:all-the-icons.el" t t)
(declare-function nerd-icons-icon-for-file "ext:nerd-icons.el" t t)
(declare-function nerd-icons-icon-for-mode "ext:nerd-icons.el" t t)
;;
;;; Faces
(defface centaur-tabs-default
'((t (:background "black" :foreground "black")))
"Default face used in the tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-unselected
'((t (:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected tab."
:group 'centaur-tabs)
(defface centaur-tabs-unselected-modified
'((t (:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected-modified tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected-modified
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected-modified tab."
:group 'centaur-tabs)
(defface centaur-tabs-close-unselected
'((t (:inherit centaur-tabs-unselected)))
"Face used for unselected close button."
:group 'centaur-tabs)
(defface centaur-tabs-close-selected
'((t (:inherit centaur-tabs-selected)))
"Face used for selected close button."
:group 'centaur-tabs)
(defface centaur-tabs-name-mouse-face
'((t nil))
"Face used for tab name when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-close-mouse-face
'((t (:inherit underline)))
"Face used for close button when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-selected
`((t (:inherit centaur-tabs-selected)))
"Face used for selected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-unselected
`((t (:inherit centaur-tabs-unselected)))
"Face used for unselected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-active-bar-face
'((t (:background "cyan")))
"Face used for selected tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-selected
'((t (:inherit centaur-tabs-modified-marker-selected :weight extra-bold)))
"Face used for selected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-unselected
'((t (:inherit centaur-tabs-modified-marker-unselected :weight extra-bold)))
"Face used for unselected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-dim-buffer-face
'((t (:foreground "gray40")))
"Face for the buffer when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
;;
;;; Tabs' display line
(defvar centaur-tabs-display-line
(if (boundp 'tab-line-format)
'tab-line
'header-line))
(defvar centaur-tabs-display-line-format
(if (boundp 'tab-line-format)
'tab-line-format
'header-line-format))
;;
;;; Tabs' characteristics
(defcustom centaur-tabs-style "bar"
"The style of tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-label-fixed-length 0
"Fixed length of label. Set to 0 if dynamic."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-background-color
(face-background 'centaur-tabs-default nil 'default)
"*Background color of the tab bar.
By default, use the background color specified for the
`centaur-tabs-default' face (or inherited from another face), or the
background color of the `default' face otherwise."
:group 'centaur-tabs
:type 'face)
(defcustom centaur-tabs-height 22
"The height of tab."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-bar-height (+ 8 centaur-tabs-height)
"The height of bar."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-mouse-pointer 'hand
"Cursor to display when hovering the tabs.
Default is `'hand'. The following scopes are possible:
- arrow
- hand
- vdrag
- hdrag
- modeline
- hourglass"
:group 'centaur-tabs
:type 'variable)
(defcustom centaur-tabs-set-bar nil
"When non nil, display a bar to show the currently selected tab.
There are three options:
- `'left': displays the bar at the left of the currently selected tab.
- `'under': displays the bar under the currently selected tab.
- `'over': displays the bar over the currently selected tab."
:group 'centaur-tabs
:type '(choice :tag "Display bar at..."
(const :tag "Put bar on the left" left)
(const :tag "Put bar as an underline" under)
(const :tag "Put bar as an overline" over)))
;;
;;; Icons
(defcustom centaur-tabs-set-icons nil
"When non nil, display an icon based on `centaur-tabs-icon-type' alongside
the tab name."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icon-type (and centaur-tabs-set-icons
(or (require 'all-the-icons nil t)
(require 'nerd-icons nil t)))
"Icon type; it should be one of `all-the-icons' and `nerd-icons'."
:group 'centaur-tabs
:type 'symbol
:set
(lambda (k v)
(pcase v
('all-the-icons
(unless (require 'all-the-icons nil t)
(setq v nil)))
('nerd-icons
(unless (require 'nerd-icons nil t)
(setq v nil)))
('type
(if (require 'all-the-icons nil t)
(setq v 'all-the-icons)
(setq v nil))))
(set k v)))
(defvar centaur-tabs-icon-scale-factor 1.0
"The base scale factor for the `height' face property of tab icons.")
(defvar centaur-tabs-icon-v-adjust 0.01
"The vertical adjust for tab icons.")
(defcustom centaur-tabs-gray-out-icons nil
"When non nil, enable gray icons for unselected buffer."
:group 'centaur-tabs
:type '(choice :tag "Gray out icons for unselected..."
(const :tag "Buffer" buffer)))
(defcustom centaur-tabs-plain-icons nil
"When non nil, tab icons' color will be the same as tabs' foreground color."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icons-prefix " "
"Prefix string before icons."
:group 'centaur-tabs
:type 'string)
(defun centaur-tabs--icon-for-file (file &rest args)
"Get the formatted icon for FILE.
ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-file file args))
('nerd-icons (apply #'nerd-icons-icon-for-file file args))))
(defun centaur-tabs--icon-for-mode (mode &rest args)
"Get the formatted icon for MODE.
ARGS should be a plist containining `:height', `:v-adjust' or `:face' properties
like in the normal icon inserting functions."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-mode mode args))
('nerd-icons (apply #'nerd-icons-icon-for-mode mode args))))
(defun centaur-tabs-icon (tab face selected)
"Generate icon for TAB using FACE's background.
If icon gray out option enabled, gray out icon if not SELECTED."
(if centaur-tabs-icon-type
(with-current-buffer (car tab)
(let* ((icon
(or (ignore-errors
(centaur-tabs--icon-for-file
(file-name-nondirectory (buffer-file-name))
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor))
(ignore-errors
(centaur-tabs--icon-for-mode
major-mode
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor))))
(background (face-background face nil 'default))
(inactive (cond ((and (not selected)
(eq centaur-tabs-gray-out-icons 'buffer))
(face-foreground 'mode-line-inactive nil 'default))
(centaur-tabs-plain-icons
(face-foreground 'centaur-tabs-selected nil 'default))
(t 'unspecified)))
(underline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'under)
(face-attribute face :underline)))
(overline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'over)
(face-attribute face :overline))))
(if (stringp icon)
(progn
(propertize icon 'face `(:inherit ,(get-text-property 0 'face icon)
:foreground ,inactive
:background ,background
:underline ,underline
:overline ,overline)))
"")))
""))
;;
;;; Ace-window style tab switching
(defcustom centaur-tabs-show-jump-identifier 'prompted
"Whether to show the tab identifier for centaur-tabs-ace-jump.
It has 3 options:
- `'nil', never show the jump identifier.
- `'prompted', only show it when using centaur-tabs-ace-jump.
- `'always', always show it regardless of the status."
:group 'centaur-tabs
:type '(choice :tag "show identifier when..."
(const :tag "Never" nil)
(const :tag "Only when prompted" prompted)
(const :tag "Always" always)))
(defcustom centaur-tabs-ace-jump-dim-buffer t
"Whether to dim the current buffer when centaur-ace-jump is activated."
:type 'boolean
:group 'centaur-tabs)
(defvar centaur-tabs-ace-jump-keys
'(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)
"Buffer jump keys used by centaur-tabs-ace-jump.")
(defvar centaur-tabs-ace-dispatch-alist
'((?q exit "Exit")
(?\C-g exit "Exit")
(?j jump-to-tab "Jump to tab")
(?x close-tab "Close tab")
(?s swap-tab "Swap tab")
(?\[ backward-group "Previous group")
(?\] forward-group "Next group")
(?? show-help "Show dispatch help"))
"Action keys used by centaur-tabs-ace-jump.
The value of each element must be in the form:
\(key keyword docstring), where keyword must be one of the follows:
\(exit, jump-to-tab, close-tab, swap-tab, backward-group,
forward-group, show-help).")
;;
;;; Close buttons, modified marker and edges' margins
(defcustom centaur-tabs-set-close-button t
"When non nil, display a clickable close button on the right side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-set-left-close-button nil
"When non nil, display a clickable close button on the left side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-close-button (make-string 1 #x00D7)
"Display appearance of the close buttons, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-set-modified-marker nil
"When non nil, display a marker when the buffer is modified."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-modified-marker (make-string 1 #x23FA)
"Display appearance of the modified marker, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-left-edge-margin " "
"Text to display at the left edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-right-edge-margin " "
"Text to display at the right edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
;;
;;; Selected tab bar
(defun centaur-tabs--make-xpm (face width height)
"Create an XPM bitmap via FACE WIDTH and HEIGHT.
Taken from `doom-modeline'."
(when (and (display-graphic-p)
(image-type-available-p 'xpm))
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or (face-background face nil t) "None")))
(ignore-errors
(create-image
(concat
(format
"/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
(length (car data)) (length data) color color)
(apply #'concat
(cl-loop with idx = 0
with len = (length data)
for dl in data
do (cl-incf idx)
collect
(concat
"\""
(cl-loop for d in dl
if (= d 0) collect (string-to-char " ")
else collect (string-to-char "."))
(if (eq idx len) "\"};" "\",\n")))))
'xpm t :ascent 'center))))))
(defvar centaur-tabs-active-bar
(centaur-tabs--make-xpm 'centaur-tabs-active-bar-face
2
centaur-tabs-bar-height))
;;
;;; Navigation buttons
(defcustom centaur-tabs-show-navigation-buttons nil
"When non-nil, show the buttons for backward/forward tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-down-tab-text ""
"Text icon to show in the down button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-backward-tab-text ""
"Text icon to show in the backward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-forward-tab-text ""
"Text icon to show in the forward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-show-count nil
"When non-nil, show the current index and count of tabs in the current group."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-count-format " [%d/%d] "
"Format text to display count."
:group 'centaur-tabs
:type 'string)
;;
;;; New tab button
(defcustom centaur-tabs-show-new-tab-button t
"When non-nil, show the button to create a new tab."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-new-tab-text " + "
"Text icon to show in the new-tab button."
:group 'centaur-tabs
:type 'string)
;;
;;; Separators
(defvar centaur-tabs-style-left nil)
(defvar centaur-tabs-style-right nil)
(defvar ns-use-srgb-colorspace)
(defvar centaur-tabs-image-apple-rgb
(and (eq (window-system) 'ns)
ns-use-srgb-colorspace
(< 11
(string-to-number
(and (string-match "darwin\\([0-9]+\\)" system-configuration)
(match-string-no-properties 1 system-configuration)))))
"Boolean variable to determine whether to use Apple RGB colorspace.
used to render images.
t on macOS 10.7+ and `ns-use-srgb-colorspace' is t, nil otherwise.
This variable is automatically set, there's no need to modify it.")
(defun centaur-tabs-separator-interpolate (color1 color2)
"Interpolate between COLOR1 and COLOR2.
COLOR1 and COLOR2 must be supplied as hex strings with a leading #."
(let* ((c1 (color-name-to-rgb color1))
(c2 (color-name-to-rgb color2))
(red (/ (+ (nth 0 c1) (nth 0 c2)) 2))
(green (/ (+ (nth 1 c1) (nth 1 c2)) 2))
(blue (/ (+ (nth 2 c1) (nth 2 c2)) 2)))
(color-rgb-to-hex red green blue)))
(defun centaur-tabs-separator-color-xyz-to-apple-rgb (X Y Z)
"Convert CIE X Y Z colors to Apple RGB color space."
(let ((r (+ (* 3.2404542 X) (* -1.5371385 Y) (* -0.4985314 Z)))
(g (+ (* -0.9692660 X) (* 1.8760108 Y) (* 0.0415560 Z)))
(b (+ (* 0.0556434 X) (* -0.2040259 Y) (* 1.0572252 Z))))
(list (expt r (/ 1.8)) (expt g (/ 1.8)) (expt b (/ 1.8)))))
(defun centaur-tabs-separator-color-srgb-to-apple-rgb (red green blue)
"Convert RED GREEN BLUE colors from sRGB color space to Apple RGB.
RED, GREEN and BLUE should be between 0.0 and 1.0, inclusive."
(apply #'centaur-tabs-separator-color-xyz-to-apple-rgb (color-srgb-to-xyz red green blue)))
(defun centaur-tabs-separator-hex-color (color)
"Get the hexadecimal value of COLOR."
(when color
(let ((srgb-color (color-name-to-rgb color)))
(if centaur-tabs-image-apple-rgb
(apply #'color-rgb-to-hex (apply #'centaur-tabs-separator-color-srgb-to-apple-rgb srgb-color))
(apply #'color-rgb-to-hex srgb-color)))))
(defun centaur-tabs-separator-pattern (lst)
"Turn LST into an infinite pattern."
(when lst
(let ((pattern (cl-copy-list lst)))
(setcdr (last pattern) pattern))))
(defun centaur-tabs-separator-pattern-to-string (pattern)
"Convert a PATTERN into a string that can be used in an XPM."
(concat "\"" (mapconcat #'number-to-string pattern "") "\","))
(defun centaur-tabs-separator-reverse-pattern (pattern)
"Reverse each line in PATTERN."
(mapcar 'reverse pattern))
(defun centaur-tabs-separator-row-pattern (fill total &optional fade)
"Make a list that has FILL 0s out of TOTAL 1s with FADE 2s to the right of
the fill."
(unless fade (setq fade 0))
(let ((fill (min fill total))
(fade (min fade (max (- total fill) 0))))
(append (make-list fill 0)
(make-list fade 2)
(make-list (- total fill fade) 1))))
(defun centaur-tabs-separator-pattern-bindings-body (patterns height-exp pattern-height-sym
second-pattern-height-sym)
"Create let-var bindings and a function body from PATTERNS.
The `car' and `cdr' parts of the result can be passed to the
function `centaur-tabs-separator-wrap-defun' as its `let-vars'
and `body' arguments,respectively. HEIGHT-EXP is an expression
calculating the image height and it should contain a free variable `height'.
PATTERN-HEIGHT-SYM and SECOND-PATTERN-HEIGHT-SYM are symbols used
for let-var binding variables."
(let* ((pattern (centaur-tabs-separator-pattern (mapcar 'centaur-tabs-separator-pattern-to-string (car patterns))))
(header (mapcar 'centaur-tabs-separator-pattern-to-string (nth 1 patterns)))
(footer (mapcar 'centaur-tabs-separator-pattern-to-string (nth 2 patterns)))
(second-pattern (centaur-tabs-separator-pattern (mapcar 'centaur-tabs-separator-pattern-to-string (nth 3 patterns))))
(center (mapcar 'centaur-tabs-separator-pattern-to-string (nth 4 patterns)))
(reserve (+ (length header) (length footer) (length center))))
(when pattern
(cons `((,pattern-height-sym (max (- ,height-exp ,reserve) 0))
(,second-pattern-height-sym (/ ,pattern-height-sym 2))
(,pattern-height-sym ,(if second-pattern `(ceiling ,pattern-height-sym 2) `,pattern-height-sym)))
(list (when header `(mapconcat 'identity ',header ""))
`(mapconcat 'identity
(cl-subseq ',pattern 0 ,pattern-height-sym) "")
(when center `(mapconcat 'identity ',center ""))
(when second-pattern
`(mapconcat 'identity
(cl-subseq ',second-pattern
0 ,second-pattern-height-sym) ""))
(when footer `(mapconcat 'identity ',footer "")))))))
(defun centaur-tabs-separator-pattern-defun (name dir width &rest patterns)
"Create a powerline function of NAME in DIR with WIDTH for PATTERNS.
PATTERNS is of the form (PATTERN HEADER FOOTER SECOND-PATTERN CENTER
PATTERN-2X HEADER-2X FOOTER-2X SECOND-PATTERN-2X CENTER-2X).
PATTERN is required, all other components are optional.
The first 5 components are for the standard resolution image.
The remaining ones are for the high resolution image where both
width and height are doubled. If PATTERN-2X is nil or not given,
then the remaining components are ignored and the standard
resolution image with magnification and interpolation will be
used in high resolution environments
All generated functions generate the form:
HEADER
PATTERN ...
CENTER
SECOND-PATTERN ...
FOOTER
PATTERN and SECOND-PATTERN repeat infinitely to fill the space needed to
generate a full height XPM.
PATTERN, HEADER, FOOTER, SECOND-PATTERN, CENTER are of the form
\((COLOR ...) (COLOR ...) ...).
COLOR can be one of 0, 1, or 2, where 0 is the source color, 1 is the
destination color, and 2 is the interpolated color between 0 and 1."
(when (eq dir 'right)
(setq patterns (mapcar 'centaur-tabs-separator-reverse-pattern patterns)))
(let ((bindings-body (centaur-tabs-separator-pattern-bindings-body patterns
'height
'pattern-height
'second-pattern-height))
(bindings-body-2x (centaur-tabs-separator-pattern-bindings-body (nthcdr 5 patterns)
'(* height 2)
'pattern-height-2x
'second-pattern-height-2x)))
(centaur-tabs-separator-wrap-defun name dir width
(append (car bindings-body) (car bindings-body-2x))
(cdr bindings-body) (cdr bindings-body-2x))))
(defun centaur-tabs-separator-background-color (face)
"Set the separator background color using FACE."
(face-attribute face
(if (face-attribute face :inverse-video nil 'default)
:foreground
:background)
nil
'default))
(defun centaur-tabs-separator-wrap-defun (name dir width let-vars body &optional body-2x)
"Generate a powerline function of name NAME in dir DIR.
This is made with WIDTH using LET-VARS and BODY.
BODY-2X is an optional argument."
(let* ((src-face (if (eq dir 'left) 'face1 'face2))
(dst-face (if (eq dir 'left) 'face2 'face1)))
`(defun ,(intern (format "powerline-%s-%s" name (symbol-name dir)))
(face1 face2 &optional height)
(when window-system
(unless height (setq height centaur-tabs-height))
(let* ,(append `((color1 (when ,src-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,src-face))))
(color2 (when ,dst-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,dst-face))))
(colori (when (and color1 color2) (centaur-tabs-separator-interpolate color1 color2)))
(color1 (or color1 "None"))
(color2 (or color2 "None"))
(colori (or colori "None")))
let-vars)
(apply #'create-image
,(append `(concat (format "/* XPM */ static char * %s_%s[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
,width
height
color1
color2
colori))
body
'("};"))
'xpm t
:ascent 'center
:face (when (and face1 face2)
,dst-face)
,(and body-2x
`(and (featurep 'mac)
(list :data-2x
,(append `(concat (format "/* XPM */ static char * %s_%s_2x[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
(* ,width 2)
(* height 2)
color1
color2
colori))
body-2x
'("};")))))))))))
(defun centaur-tabs-separator-alternate (dir)
"Generate an alternating pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "alternate" dir 4
'((2 2 1 1)
(0 0 2 2))
nil nil nil nil
;; 2x
'((2 2 2 2 1 1 1 1)
(2 2 2 2 1 1 1 1)
(0 0 0 0 2 2 2 2)
(0 0 0 0 2 2 2 2))))
(defun centaur-tabs-separator-bar (dir)
"Generate a bar XPM function for DIR."
(centaur-tabs-separator-pattern-defun "bar" dir 2
'((2 2))))
(defun centaur-tabs-separator-box (dir)
"Generate a box XPM function for DIR."
(centaur-tabs-separator-pattern-defun "box" dir 2
'((0 0)
(0 0)
(1 1)
(1 1))
nil nil nil nil
;; 2x
'((0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1))))
(defun centaur-tabs-separator-chamfer (dir)
"Generate a chamfer XPM function for DIR."
(centaur-tabs-separator-pattern-defun "chamfer" dir 3
'((0 0 0))
'((1 1 1)
(0 1 1)
(0 0 1))
nil nil nil
;; 2x
'((0 0 0 0 0 0))
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1))))
(defun centaur-tabs-separator-rounded (dir)
"Generate a rounded XPM function for DIR."
(centaur-tabs-separator-pattern-defun "rounded" dir 6
'((0 0 0 0 0 0))
'((2 1 1 1 1 1)
(0 0 2 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 2 1)
(0 0 0 0 0 1)
(0 0 0 0 0 2))
nil nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0))
'((1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 2 1 1 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 2 1)
(0 0 0 0 0 0 0 0 0 0 0 1)
(0 0 0 0 0 0 0 0 0 0 0 1))))
(defun centaur-tabs-separator-slant (dir)
"Generate a slant XPM function for DIR."
(let* ((row-modifier (if (eq dir 'left) 'identity 'reverse)))
(centaur-tabs-separator-wrap-defun "slant" dir 'width
'((width (1- (ceiling height 2))))
`((cl-loop for i from 0 to (1- height)
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) width)))))
`((cl-loop for i from 0 to (1- (* height 2))
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) (* width 2)))))))))
(defun centaur-tabs-separator-wave (dir)
"Generate a wave XPM function for DIR."
(centaur-tabs-separator-pattern-defun "wave" dir 11
'((0 0 0 0 0 0 1 1 1 1 1))
'((2 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1))
'((0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 2 1 1)
(0 0 0 0 0 0 0 0 0 0 2))
nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1))
'((1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1))
'((0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))))
(defun centaur-tabs-separator-zigzag (dir)
"Generate a zigzag pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "zigzag" dir 3
'((1 1 1)
(0 1 1)
(0 0 1)
(0 0 0)
(0 0 1)
(0 1 1))
nil nil nil nil
;; 2x
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1)
(0 0 0 0 0 0)
(0 0 0 0 0 1)
(0 0 0 0 1 1)
(0 0 0 1 1 1)
(0 0 1 1 1 1)
(0 1 1 1 1 1))))
(defun centaur-tabs-separator-memoize (func)
"Memoize FUNC.
If argument is a symbol then install the memoized function over
the original function. Use frame-local memoization."
(cl-typecase func
(symbol (fset func (centaur-tabs-separator-memoize-wrap-frame-local (symbol-function func))) func)
(function (centaur-tabs-separator-memoize-wrap-frame-local func))))
(defun centaur-tabs-separator-memoize-wrap-frame-local (func)
"Return the memoized version of FUNC.
The memoization cache is frame-local."
(let ((funcid (cl-gensym)))
`(lambda (&rest args)
,(concat (documentation func) (format "\n(memoized function %s)" funcid))
(let* ((cache (centaur-tabs-separator-create-or-get-cache))
(key (cons ',funcid args))
(val (gethash key cache)))
(or val
(puthash key (apply ,func args) cache))))))
(defun centaur-tabs-separator-create-or-get-cache ()
"Return a frame-local hash table that acts as a memoization cache.
The cache is for the powerline.
Create one if the frame doesn't have one yet."
(if-let* ((table (frame-parameter nil 'powerline-cache))
((hash-table-p table)))
table
(centaur-tabs-separator-reset-cache)))
(defun centaur-tabs-separator-reset-cache ()
"Reset and return the frame-local hash table used for a memoization cache."
(let ((table (make-hash-table :test 'equal)))
;; Store it as a frame-local variable
(modify-frame-parameters nil `((powerline-cache . ,table)))
table))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'right))
(defun centaur-tabs-select-separator-style (tab-style)
"Set the separator style to TAB-STYLE."
(let* ((theme (or (car custom-enabled-themes) "default"))
(name (intern (format "centaur-tabs--%s-%s-face" theme tab-style)))
(face (copy-face 'centaur-tabs-default name)))
(setq centaur-tabs-style-left
(funcall (intern (format "powerline-%s-right" tab-style))
face nil centaur-tabs-height))
(setq centaur-tabs-style-right
(funcall (intern (format "powerline-%s-left" tab-style))
nil face centaur-tabs-height))))
(provide 'centaur-tabs-elements)
;;; centaur-tabs-elements.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,642 @@
;;; centaur-tabs-interactive.el --- centaur-tabs interactive functions and plugins support lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; This file is not part of GNU Emacs.
;; 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 2, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; This file contains centaur-tabs interactive functions and plugins support
;;
;;; Code:
(require 'cl-lib)
(require 'centaur-tabs-elements)
;; Compiler pacifier
(declare-function ivy-read "ext:ivy.el" t t)
(declare-function helm-build-sync-source "ext:helm-source.el" t t)
(defvar helm-source-centaur-tabs-group)
(declare-function projectile-project-root "ext:projectile.el" t t)
(declare-function projectile-project-name "ext:projectile.el" t t)
(declare-function centaur-tabs-do-select "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tab-from-event "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-value "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-show-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-close-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-view "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-extensions "centaur-tabs-functions.el")
(declare-function centaur-tabs-selected-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-display-update "centaur-tabs-functions.el")
(declare-function centaur-tabs-set-template "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-select-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-tabs "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabsets-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-current-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-completing-read "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-tab "centaur-tabs-functions.el")
(defvar centaur-tabs-cycle-scope)
(defvar centaur-tabs-current-tabset)
(defvar centaur-tabs-last-focused-buffer-group)
(defvar centaur-tabs-buffer-list-function)
(defvar centaur-tabs-buffer-groups-function)
(defvar centaur-tabs--buffer-show-groups)
;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;
(defun centaur-tabs-switch-group (&optional groupname)
"Switch tab groups using ido. GROUPNAME can optionaly be provided."
(interactive)
(let* ((tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall centaur-tabs-buffer-groups-function) )))
(funcall centaur-tabs-buffer-list-function)))
(groups (centaur-tabs-get-groups))
(group-name (or groupname (centaur-tabs-completing-read "Groups: " groups))) )
(catch 'done
(mapc #'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun centaur-tabs-select-end-tab ()
"Select end tab of current tabset."
(interactive)
(centaur-tabs-select-beg-tab t))
(defun centaur-tabs-select-beg-tab (&optional backward)
"Select beginning tab of current tabs.
If BACKWARD is non-nil, move backward, otherwise move forward.
TYPE is default option."
(interactive)
(let* ((tabset (centaur-tabs-current-tabset t))
(ttabset (centaur-tabs-get-tabsets-tabset))
(_cycle (if (and (eq centaur-tabs-cycle-scope 'groups)
(not (cdr (centaur-tabs-tabs ttabset))))
'tabs
centaur-tabs-cycle-scope))
_selected tab)
(when tabset
(setq tabset (centaur-tabs-tabs tabset)
tab (car (if backward (last tabset) tabset)))
(centaur-tabs-buffer-select-tab tab))))
(defun centaur-tabs-backward-tab-other-window (&optional reversed)
"Move to left tab in other window.
Optional argument REVERSED default is move backward, if reversed is non-nil
move forward."
(interactive)
(other-window 1)
(if reversed
(centaur-tabs-forward-tab)
(centaur-tabs-backward-tab))
(other-window -1))
(defun centaur-tabs-forward-tab-other-window ()
"Move to right tab in other window."
(interactive)
(centaur-tabs-backward-tab-other-window t))
(defun centaur-tabs-move-current-tab-to-right ()
"Move current tab one place right, unless it's already the rightmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(new-bufs (list))
the-buffer)
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push (car old-bufs) new-bufs)
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(setq the-buffer (car old-bufs))
(setq old-bufs (cdr old-bufs))
(if old-bufs ; if this is false, then the current tab is the rightmost
(push (car old-bufs) new-bufs))
(push the-buffer new-bufs)) ; this is the tab that was to be moved
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs)))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update)))
(defun centaur-tabs-move-current-tab-to-left ()
"Move current tab one place left, unless it's already the leftmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(first-buf (car old-bufs))
(new-bufs (list))
not-yet-this-buf)
(if (string= (buffer-name) (format "%s" (car first-buf)))
old-bufs ; the current tab is the leftmost
(setq not-yet-this-buf first-buf)
(setq old-bufs (cdr old-bufs))
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push not-yet-this-buf new-bufs)
(setq not-yet-this-buf (car old-bufs))
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(push (car old-bufs) new-bufs) ; this is the tab that was to be moved
(push not-yet-this-buf new-bufs)
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs))))
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update))))
(defmacro centaur-tabs-kill-buffer-match-rule (match-rule)
"If buffer match MATCH-RULE, kill it."
`(save-excursion
(mapc #'(lambda (buffer)
(with-current-buffer buffer
(when (string-equal current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(when (funcall ,match-rule buffer)
(kill-buffer buffer)))))
(buffer-list))))
(defun centaur-tabs-kill-all-buffers-in-current-group ()
"Kill all buffers in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t)))))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (_buffer) t))
;; Switch to next group.
(centaur-tabs-forward-group)))
(defun centaur-tabs-kill-other-buffers-in-current-group ()
"Kill all buffers except current buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (equal buffer currentbuffer))))))
(defun centaur-tabs-kill-unmodified-buffers-in-current-group ()
"Kill all unmodified buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (buffer-modified-p buffer))))))
(defun centaur-tabs-kill-match-buffers-in-current-group ()
"Kill all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Kill buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (string-equal (file-name-extension filename) match-extension)))))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(defun centaur-tabs-keep-match-buffers-in-current-group ()
"Keep all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Just keep buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (not (string-equal (file-name-extension filename) match-extension))))))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(defun centaur-tabs-select-visible-nth-tab (tab-index)
"Select visible tab with TAB-INDEX'.
Example, when `tab-index' is 1, this function will select the leftmost label in
the visible area, instead of the first label in the current group.
If `tab-index' more than length of visible tabs, selet the last tab.
If `tab-index' is 0, select last tab."
(let ((visible-tabs (centaur-tabs-view centaur-tabs-current-tabset)))
(switch-to-buffer
(car
(if (or (equal tab-index 0)
(> tab-index (length visible-tabs)))
(car (last visible-tabs))
(nth (- tab-index 1) visible-tabs))))))
(defun centaur-tabs-select-visible-tab ()
"Bind this function with number keystroke, such as s-1, s-2, s-3 ... etc.
This function automatically recognizes the number at the end of the keystroke
and switches to the tab of the corresponding index.
Note that this function switches to the visible range,
not the actual logical index position of the current group."
(interactive)
(let* ((event last-command-event)
(key (make-vector 1 event))
(key-desc (key-description key)))
(centaur-tabs-select-visible-nth-tab
(string-to-number (car (last (split-string key-desc "-")))))))
;; ace-jump style tab switching
(defvar centaur-tabs-ace-jump-active nil
"Set to t if `centaur-tabs-ace-jump' is invoked.")
(defvar centaur-tabs-dim-overlay nil
"Holds the overlay for dimming buffer when invoking centaur-tabs-ace-jump.")
(defun centaur-tabs--dim-window ()
"Create a dim background overlay for the current window."
(when centaur-tabs-ace-jump-dim-buffer
(when centaur-tabs-dim-overlay
(delete-overlay centaur-tabs-dim-overlay))
(setq centaur-tabs-dim-overlay
(let ((ol (make-overlay (window-start) (window-end))))
(overlay-put ol 'face 'centaur-tabs-dim-buffer-face)
ol))))
(defun centaur-tabs-swap-tab (tab)
"Swap the position of current tab with TAB.
TAB has to be in the same group as the current tab."
(if (eq (centaur-tabs-tab-tabset tab) (centaur-tabs-current-tabset t))
(let* ((group (centaur-tabs-current-tabset t))
(tabs (cl-copy-list (centaur-tabs-tabs group)))
(current (centaur-tabs-selected-tab group))
(current-index (cl-position current tabs))
(target-index (cl-position tab tabs)))
(if (eq tab current)
(message "Can't swap with current tab itself.")
(setcar (nthcdr current-index tabs) tab)
(setcar (nthcdr target-index tabs) current)
(set group tabs)
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)))
(message "Error: %s is not in the same group as the current tab." tab)))
(defun centaur-tabs-ace-action (action)
"Preform ACTION on a visible tab. Ace-jump style.
ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
(when (centaur-tabs-current-tabset t)
(when centaur-tabs-ace-jump-dim-buffer
(centaur-tabs--dim-window))
(cond ((eq action 'jump-to-tab)
(message "Jump to tab: "))
((eq action 'close-tab)
(message "Close tab: "))
((eq action 'swap-tab)
(message "Swap current tab with: ")))
(let ((centaur-tabs-ace-jump-active t))
(catch 'done
(while t
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)
(let ((char (read-key)) (action-cache))
(cond
;; tab keys
((memq char centaur-tabs-ace-jump-keys)
(let ((sel (nth (cl-position char centaur-tabs-ace-jump-keys) (centaur-tabs-view (centaur-tabs-current-tabset t)))))
(cond ((eq sel nil)
(message "Tab %s does not exist" (key-description (vector char))))
((eq action 'jump-to-tab)
(centaur-tabs-buffer-select-tab sel))
((eq action 'close-tab)
(centaur-tabs-buffer-close-tab sel))
((eq action 'swap-tab)
(centaur-tabs-swap-tab sel))))
(throw 'done nil))
;; actions
((setq action-cache (assoc char centaur-tabs-ace-dispatch-alist))
(setq action-cache (cadr action-cache))
(cond ((eq action-cache 'exit) ; exit
(message "Quit")
(throw 'done nil))
((eq action-cache 'forward-group) ; forward group
(message "Forward group")
(centaur-tabs-forward-group)
(centaur-tabs--dim-window))
((eq action-cache 'backward-group) ; backward group
(message "Backward group")
(centaur-tabs-backward-group)
(centaur-tabs--dim-window))
((eq action-cache 'show-help) ; help menu
(message "%s" (mapconcat
(lambda (elem) (format "%s: %s"
(key-description (vector (car elem)))
(caddr elem)))
centaur-tabs-ace-dispatch-alist
"\n")))
(t (setq action action-cache) ; other actions
(cond ((eq action-cache 'jump-to-tab)
(message "Jump to tab: "))
((eq action-cache 'close-tab)
(message "Close tab: "))
((eq action-cache 'swap-tab)
(message "Swap current tab with: "))))))
;; no match, repeat
(t
(message "No such candidate: %s, hit ? for help." (key-description (vector char)))))))))
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(when centaur-tabs-ace-jump-dim-buffer
(delete-overlay centaur-tabs-dim-overlay)
(setq centaur-tabs-dim-overlay nil))
(centaur-tabs-display-update)))
(defun centaur-tabs-ace-jump (&optional arg)
"Select a tab and perform an action. Ace-jump style.
If no ARG is provided, select that tab. If prefixed with one
`universal-argument', swap the current tab with the selected tab.
If prefixed with two `universal-argument's, close selected tab."
(interactive "p")
(cond ((eq arg 1)
(centaur-tabs-ace-action 'jump-to-tab))
((eq arg 4)
(centaur-tabs-ace-action 'swap-tab))
((eq arg 16)
(centaur-tabs-ace-action 'close-tab))
(t
(centaur-tabs-ace-action 'jump-to-tab))))
(defun centaur-tabs-group-buffer-groups ()
"Use centaur-tabs's own buffer grouping function."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-buffer-groups)
(centaur-tabs-display-update))
;; Projectile integration. Taken from tabbar-ruler
(defvar centaur-tabs-projectile-buffer-group-calc nil
"Set buffer groups for projectile.
Should be buffer local and speed up calculation of buffer groups.")
(defun centaur-tabs-projectile-buffer-groups ()
"Return the list of group names BUFFER belongs to."
(if centaur-tabs-projectile-buffer-group-calc
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)
(set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc)
(cond
((or (get-buffer-process (current-buffer)) (memq major-mode '(comint-mode compilation-mode))) '("Term"))
((string-equal "*" (substring (buffer-name) 0 1)) '("Misc"))
((condition-case _err
(projectile-project-root)
(error nil))
(list (projectile-project-name)))
((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode
c++-mode javascript-mode js-mode
js2-mode makefile-mode
lua-mode vala-mode))
'("Coding"))
((memq major-mode '( nxhtml-mode html-mode
mhtml-mode css-mode))
'("HTML"))
((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org"))
((memq major-mode '(dired-mode)) '("Dir"))
(t '("Other"))))
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)))
(defun centaur-tabs-group-by-projectile-project()
"Group by projectile project."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-projectile-buffer-groups)
(centaur-tabs-display-update))
;; Show groups instead of tabs
(defun centaur-tabs-toggle-groups ()
"Show group names on the tabs instead of buffer names."
(interactive)
(centaur-tabs-buffer-show-groups (not centaur-tabs--buffer-show-groups))
(centaur-tabs-display-update))
;; Helm source for switching group in helm.
(defun centaur-tabs-build-helm-source ()
"Display a list of current buffer groups in Helm."
(interactive)
(setq helm-source-centaur-tabs-group
(when (featurep 'helm)
(require 'helm)
(helm-build-sync-source "Centaur-Tabs Group"
:candidates #'centaur-tabs-get-groups
:action '(("Switch to group" . centaur-tabs-switch-group))))))
;; Ivy source for switching group in ivy.
;;;###autoload
(defun centaur-tabs-counsel-switch-group ()
"Display a list of current buffer groups using Counsel."
(interactive)
(when (featurep 'ivy)
(require 'ivy)
(ivy-read
"Centaur Tabs Groups:"
(centaur-tabs-get-groups)
:action #'centaur-tabs-switch-group
:caller 'centaur-tabs-counsel-switch-group)))
(defun centaur-tabs-extract-window-to-new-frame()
"Kill the current window in the current frame, and open the current buffer
in a new frame."
(interactive)
(unless (centaur-tabs--one-window-p)
(let ((buffer (current-buffer)))
(delete-window)
(display-buffer-pop-up-frame buffer nil))))
(defun centaur-tabs--copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
;;; From https://emacsredux.com/blog/2013/03/27/copy-filename-to-the-clipboard/
(interactive)
(let* ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name)))
(filename (expand-file-name filename)))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the kill ring." filename))))
(defun centaur-tabs-open-directory-in-external-application ()
"Open the current directory in a external application."
(interactive)
(centaur-tabs--open-externally default-directory))
(defun centaur-tabs-open-in-external-application ()
"Open the file of the current buffer according to its mime type."
(interactive)
(let ((path (or (buffer-file-name) default-directory)))
(centaur-tabs--open-externally path)))
(defun centaur-tabs--open-externally (file-or-path)
"Open FILE-OR-PATH according to its mime type in an external application.
FILE-OR-PATH is expanded with `expand-file-name`.
Modified copy of `treemacs-visit-node-in-external-application`."
(let ((path (expand-file-name file-or-path)))
(pcase system-type
('windows-nt
(declare-function w32-shell-execute "w32fns.c")
(w32-shell-execute "open" (replace-regexp-in-string "/" "\\" path t t)))
('darwin
(shell-command (format "open \"%s\"" path)))
('gnu/linux
(let ((process-connection-type nil))
(start-process "" nil "xdg-open" path)))
(_ (message "Don't know how to open files on %s." (symbol-name system-type))))))
(defun centaur-tabs--copy-directory-name-to-clipboard ()
"Copy the current directory name to the clipboard."
(interactive)
(when default-directory
(kill-new default-directory)
(message "Copied directory name '%s' to the kill ring." (expand-file-name default-directory))))
(defun centaur-tabs--tab-submenu-groups-definition ()
"Menu definition with a list of tab groups."
(mapcar (lambda (s) `[,s ,s]) (sort (centaur-tabs-get-groups) #'string<)))
(defun centaur-tabs--tab-submenu-tabs-definition ()
"Menu definition with a list of tabs for the current group."
(let* ((tabset (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))
(tabs-in-group (centaur-tabs-tabs tabset))
(buffers (mapcar #'centaur-tabs-tab-value tabs-in-group))
(sorted-tabnames (sort (mapcar #'buffer-name buffers) #'string<)))
(mapcar (lambda (s) `[,s ,s]) sorted-tabnames)))
(defvar centaur-tabs--groups-submenu-key "Tab groups")
(defvar centaur-tabs--tabs-submenu-key "Go to tab of group")
(defun centaur-tabs--kill-this-buffer-dont-ask()
"Kill the current buffer without confirmation."
(interactive)
(kill-buffer (current-buffer))
(centaur-tabs-display-update)
(redisplay t))
(defun centaur-tabs--tab-menu-definition ()
"Definition of the context menu of a tab."
`(["Kill this buffer" centaur-tabs--kill-this-buffer-dont-ask]
["Kill other buffers of group" centaur-tabs-kill-other-buffers-in-current-group]
["Kill unmodified buffers of group" centaur-tabs-kill-unmodified-buffers-in-current-group]
"----"
["Split below" split-window-below]
["Split right" split-window-right]
"----"
["Maximize tab" delete-other-windows
:active (null (centaur-tabs--one-window-p))]
["Extract to new frame" centaur-tabs-extract-window-to-new-frame
:active (null (centaur-tabs--one-window-p))]
["Duplicate in new frame" make-frame-command]
"----"
["Copy filepath" centaur-tabs--copy-file-name-to-clipboard
:active (buffer-file-name)]
["Copy directory path" centaur-tabs--copy-directory-name-to-clipboard
:active default-directory]
["Open in external application" centaur-tabs-open-in-external-application
:active (or (buffer-file-name) default-directory)]
["Open directory in dired" dired-jump
:active (not (eq major-mode 'dired-mode))]
["Open directory externally" centaur-tabs-open-directory-in-external-application
:active default-directory]
"----"
,( append (list centaur-tabs--groups-submenu-key) (centaur-tabs--tab-submenu-groups-definition))
,( append (list centaur-tabs--tabs-submenu-key) (centaur-tabs--tab-submenu-tabs-definition))))
(defun centaur-tabs--one-window-p ()
"Like `one-window-p`, but taking into account side windows like treemacs."
(let* ((mainwindow (window-main-window))
(child-count (window-child-count mainwindow)))
(= 0 child-count)))
(defun centaur-tabs--get-tab-from-name (tabname)
"Get the tab from the current group given de TABNAME."
(let ((seq (centaur-tabs-tabs (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))))
(cl-find-if
(lambda (tab) (string= tabname (buffer-name (centaur-tabs-tab-value tab))))
seq)))
(defun centaur-tabs--tab-menu (event)
"Show a context menu for the clicked tab or button.
The clicked tab, identified by EVENT, is selected."
(interactive "e" )
(let ((click-on-tab-p (ignore-errors (centaur-tabs-get-tab-from-event event))))
(when (not click-on-tab-p)
(centaur-tabs--groups-menu))
(when click-on-tab-p
(centaur-tabs-do-select event)
(redisplay t)
(let*
((menu (easy-menu-create-menu nil (centaur-tabs--tab-menu-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let* ((menu-key (cl-first choice))
(choice-is-group-p (string= centaur-tabs--groups-submenu-key (symbol-name menu-key)))
(name (car (last choice)))
(name-as-string (symbol-name name)))
(if choice-is-group-p
(centaur-tabs-switch-group name-as-string)
(switch-to-buffer name-as-string))))))))
(defun centaur-tabs--groups-menu ()
"Show a popup menu with the centaur tabs groups."
(interactive)
(let* ((sorted-groups (centaur-tabs--tab-submenu-groups-definition))
(menu (easy-menu-create-menu "Tab groups"
(centaur-tabs--tab-submenu-groups-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let ((group (car (last choice))))
(centaur-tabs-switch-group (format "%s" group))))))
(provide 'centaur-tabs-interactive)
;;; centaur-tabs-interactive.el ends here

View file

@ -0,0 +1,15 @@
(define-package "centaur-tabs" "20240726.625" "Aesthetic, modern looking customizable tabs plugin"
'((emacs "27.1")
(powerline "2.4"))
:commit "49b9f6b813dfb1fe78aa782f76b4a7333dd8f980" :authors
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainers
'(("Jen-Chieh Shen" . "jcs090218@gmail.com"))
:maintainer
'("Jen-Chieh Shen" . "jcs090218@gmail.com")
:keywords
'("frames")
:url "https://github.com/ema2159/centaur-tabs")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,230 @@
;;; centaur-tabs.el --- Aesthetic, modern looking customizable tabs plugin -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; Filename: centaur-tabs.el
;; Description: Provide an out of box configuration to use highly customizable tabs.
;; URL: https://github.com/ema2159/centaur-tabs
;; Author: Emmanuel Bustos <ema2159@gmail.com>
;; Maintainer: Jen-Chieh Shen <jcs090218@gmail.com>
;; Created: 2019-21-19 22:14:34
;; Version: 3.3
;; Known Compatibility: GNU Emacs 26.2
;; Package-Requires: ((emacs "27.1") (powerline "2.4"))
;; Keywords: frames
;;; This file is NOT part of GNU Emacs
;;; License
;;
;; 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, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; Emacs plugin aiming to become an aesthetic, modern looking tabs plugin.
;;
;; This package offers tabs with a wide range of customization options, both
;; aesthetical and functional, implementing them trying to follow the Emacs
;; philosophy packing them with useful keybindings and a nice integration
;; with the Emacs environment, without sacrificing customizability.
;; Some of the features Centaur tabs offers are:
;; - Tab styles
;; - Tab icons
;; - Graying out icons
;; - Selected tab bar (over, under and left bar)
;; - Close button
;; - Modified marker
;; - Buffer grouping
;; - Projectile integration
;; - Ivy and Helm integration for group switching
;;
;;; Code:
(require 'centaur-tabs-elements)
(require 'centaur-tabs-functions)
(require 'centaur-tabs-interactive)
;; Compiler pacifier
(declare-function undo-tree-undo-1 "ext:undo-tree.el")
(declare-function undo-tree-redo-1 "ext:undo-tree.el")
;;;;;;;;;;;;;;;;;;;;;;; Centaur-Tabs source code ;;;;;;;;;;;;;;;;;;;;;;;
(defgroup centaur-tabs nil
"Display a tab bar in the header line."
:group 'convenience)
(defvar centaur-tabs--buffer-show-groups nil)
;;
;;; Minor modes
(defsubst centaur-tabs-mode-on-p ()
"Return non-nil if Centaur-Tabs mode is on."
(eq (default-value centaur-tabs-display-line-format)
centaur-tabs-header-line-format))
;;
;;; Centaur-Tabs-Local mode
(defvar centaur-tabs--local-hlf nil)
;;;###autoload
(define-minor-mode centaur-tabs-local-mode
"Toggle local display of the tab bar.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
When turned on, if a local header line is shown, it is hidden to show
the tab bar. The tab bar is locally hidden otherwise. When turned
off, if a local header line is hidden or the tab bar is locally
hidden, it is shown again. Signal an error if Centaur-Tabs mode is off."
:group 'centaur-tabs
:global nil
(unless (centaur-tabs-mode-on-p)
(error "Centaur-Tabs mode must be enabled"))
;;; ON
(if centaur-tabs-local-mode
(if (and (local-variable-p centaur-tabs-display-line-format)
(eval centaur-tabs-display-line-format))
;; A local header line exists, hide it to show the tab bar.
(progn
;; Fail in case of an inconsistency because another local
;; header line is already hidden.
(when (local-variable-p 'centaur-tabs--local-hlf)
(error "Another local header line is already hidden"))
(set (make-local-variable 'centaur-tabs--local-hlf)
(eval centaur-tabs-display-line-format))
(kill-local-variable centaur-tabs-display-line-format))
;; Otherwise hide the tab bar in this buffer.
(set centaur-tabs-display-line-format nil))
;;; OFF
(if (local-variable-p 'centaur-tabs--local-hlf)
;; A local header line is hidden, show it again.
(progn
(set centaur-tabs-display-line-format centaur-tabs--local-hlf)
(kill-local-variable 'centaur-tabs--local-hlf))
;; The tab bar is locally hidden, show it again.
(kill-local-variable centaur-tabs-display-line-format))))
;;; Centaur-Tabs mode
;;
(defvar centaur-tabs--global-hlf nil)
;;;###autoload
(define-minor-mode centaur-tabs-mode
"Toggle display of a tab bar in the header line.
With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled.
\\{centaur-tabs-mode-map}"
:group 'centaur-tabs
:require 'centaur-tabs
:global t
:keymap centaur-tabs-mode-map
(if centaur-tabs-mode
;;; ON
(unless (centaur-tabs-mode-on-p)
;; Save current default value of `centaur-tabs-display-line-format'.
(setq centaur-tabs--global-hlf (default-value centaur-tabs-display-line-format))
(centaur-tabs-init-tabsets-store)
(set-default centaur-tabs-display-line-format centaur-tabs-header-line-format))
;;; OFF
(when (centaur-tabs-mode-on-p)
;; Turn off Centaur-Tabs-Local mode globally.
(mapc #'(lambda (b)
(condition-case nil
(with-current-buffer b
(and centaur-tabs-local-mode
(centaur-tabs-local-mode -1)))
(error nil)))
(buffer-list))
;; Restore previous `centaur-tabs-display-line-format'.
(set-default centaur-tabs-display-line-format centaur-tabs--global-hlf)
(centaur-tabs-free-tabsets-store)))
;; Make sure it refresh every windows!
(force-window-update))
;;
;;; Tab bar buffer setup
(defun centaur-tabs-buffer-init ()
"Initialize tab bar buffer data.
Run as `centaur-tabs-init-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function 'centaur-tabs-buffer-tabs
centaur-tabs-tab-label-function 'centaur-tabs-buffer-tab-label
centaur-tabs-select-tab-function 'centaur-tabs-buffer-select-tab)
;; If set, initialize selected overline
(when (eq centaur-tabs-set-bar 'under)
(set-face-attribute 'centaur-tabs-selected nil
:underline (face-background 'centaur-tabs-active-bar-face nil 'default)
:overline nil)
(set-face-attribute 'centaur-tabs-selected-modified nil
:underline (face-background 'centaur-tabs-active-bar-face nil 'default)
:overline nil)
(set-face-attribute 'centaur-tabs-unselected nil
:underline nil
:overline nil)
(set-face-attribute 'centaur-tabs-unselected-modified nil
:underline nil
:overline nil))
(when (eq centaur-tabs-set-bar 'over)
(set-face-attribute 'centaur-tabs-selected nil
:overline (face-background 'centaur-tabs-active-bar-face nil 'default)
:underline nil)
(set-face-attribute 'centaur-tabs-selected-modified nil
:overline (face-background 'centaur-tabs-active-bar-face nil 'default)
:underline nil)
(set-face-attribute 'centaur-tabs-unselected nil
:overline nil
:underline nil)
(set-face-attribute 'centaur-tabs-unselected-modified nil
:overline nil
:underline nil))
(add-function :after after-focus-change-function #'centaur-tabs-after-focus)
(add-hook 'window-buffer-change-functions #'centaur-tabs-on-window-buffer-change)
(add-hook 'after-save-hook #'centaur-tabs-on-saving-buffer)
(add-hook 'first-change-hook #'centaur-tabs-on-modifying-buffer)
(add-hook 'kill-buffer-hook #'centaur-tabs-buffer-track-killed)
(advice-add #'undo :after #'centaur-tabs-on-modifying-buffer)
(advice-add #'undo-tree-undo-1 :after #'centaur-tabs-on-modifying-buffer)
(advice-add #'undo-tree-redo-1 :after #'centaur-tabs-on-modifying-buffer)
(advice-add 'load-theme :after #'centaur-tabs--after-load-theme))
(defun centaur-tabs-buffer-quit ()
"Quit tab bar buffer.
Run as `centaur-tabs-quit-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function nil
centaur-tabs-tab-label-function nil
centaur-tabs-select-tab-function nil)
(remove-function after-focus-change-function #'centaur-tabs-after-focus)
(remove-hook 'window-buffer-change-functions #'centaur-tabs-on-window-buffer-change)
(remove-hook 'after-save-hook 'centaur-tabs-on-modifying-buffer)
(remove-hook 'first-change-hook 'centaur-tabs-on-modifying-buffer)
(remove-hook 'kill-buffer-hook 'centaur-tabs-buffer-track-killed)
(advice-remove #'undo #'centaur-tabs-on-modifying-buffer)
(advice-remove #'undo-tree-undo-1 #'centaur-tabs-on-modifying-buffer)
(advice-remove #'undo-tree-redo-1 #'centaur-tabs-on-modifying-buffer)
(advice-remove 'load-theme #'centaur-tabs--after-load-theme))
(add-hook 'centaur-tabs-init-hook #'centaur-tabs-buffer-init)
(add-hook 'centaur-tabs-quit-hook #'centaur-tabs-buffer-quit)
(provide 'centaur-tabs)
;;; centaur-tabs.el ends here

View file

@ -0,0 +1 @@
Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2022-08-25T17:05:04-0400 using RSA

View file

@ -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)))

View file

@ -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>)

View file

@ -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 <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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

View file

@ -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 <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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

View file

@ -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 <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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

View file

@ -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 <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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 cant 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

View file

@ -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 <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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 dont 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

View file

@ -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))))
;;;### (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"))
;;;***
;;;### (autoloads nil "compat-help" "compat-help.el" (0 0 0 0))
;;; Generated autoloads from compat-help.el
(register-definition-prefixes "compat-help" '("compat---describe"))
;;;***
;;;### (autoloads nil "compat-macs" "compat-macs.el" (0 0 0 0))
;;; Generated autoloads from compat-macs.el
(register-definition-prefixes "compat-macs" '("compat-"))
;;;***
;;;### (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))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; compat-autoloads.el ends here

View file

@ -0,0 +1,48 @@
;;; compat-font-lock.el --- -*- lexical-binding: t; -*-
;; Copyright (C) 2022 Free Software Foundation, Inc.
;; Author: Philip Kaludercic <philipk@posteo.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

View file

@ -0,0 +1,57 @@
;;; compat-help.el --- Documentation for compat functions -*- lexical-binding: t; -*-
;; Copyright (C) 2022 Free Software Foundation, Inc.
;; Author: Philip Kaludercic <philipk@posteo.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

View file

@ -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 <philipk@posteo.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

View file

@ -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" . "philipk@posteo.net")) :maintainer '("Compat Development" . "~pkal/compat-devel@lists.sr.ht") :keywords '("lisp") :url "https://sr.ht/~pkal/compat")

View file

@ -0,0 +1,58 @@
;;; compat.el --- Emacs Lisp Compatibility Library -*- lexical-binding: t; -*-
;; Copyright (C) 2021, 2022 Free Software Foundation, Inc.
;; Author: Philip Kaludercic <philipk@posteo.net>
;; Maintainer: Compat Development <~pkal/compat-devel@lists.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

File diff suppressed because it is too large Load diff

18
elpa/compat-28.1.2.2/dir Normal file
View file

@ -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.

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.

View file

@ -0,0 +1,83 @@
;;; dash-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dash.el
(autoload 'dash-fontify-mode "dash" "\
Toggle fontification of Dash special variables.
Dash-Fontify mode is a buffer-local minor mode intended for Emacs
Lisp buffers. Enabling it causes the special variables bound in
anaphoric Dash macros to be fontified. These anaphoras include
`it', `it-index', `acc', and `other'. In older Emacs versions
which do not dynamically detect macros, Dash-Fontify mode
additionally fontifies Dash macro calls.
See also `dash-fontify-mode-lighter' and
`global-dash-fontify-mode'.
This is a minor mode. If called interactively, toggle the
`Dash-Fontify mode' mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `dash-fontify-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(put 'global-dash-fontify-mode 'globalized-minor-mode t)
(defvar global-dash-fontify-mode nil "\
Non-nil if Global Dash-Fontify mode is enabled.
See the `global-dash-fontify-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `global-dash-fontify-mode'.")
(custom-autoload 'global-dash-fontify-mode "dash" nil)
(autoload 'global-dash-fontify-mode "dash" "\
Toggle Dash-Fontify mode in all buffers.
With prefix ARG, enable Global Dash-Fontify mode if ARG is positive;
otherwise, disable it.
If called from Lisp, toggle the mode if ARG is `toggle'.
Enable the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
Dash-Fontify mode is enabled in all buffers where
`dash--turn-on-fontify-mode' would do it.
See `dash-fontify-mode' for more information on Dash-Fontify mode.
(fn &optional ARG)" t)
(autoload 'dash-register-info-lookup "dash" "\
Register the Dash Info manual with `info-lookup-symbol'.
This allows Dash symbols to be looked up with \\[info-lookup-symbol]." t)
(register-definition-prefixes "dash" '("!cdr" "!cons" "--" "->" "-a" "-butlast" "-c" "-d" "-e" "-f" "-gr" "-i" "-juxt" "-keep" "-l" "-m" "-no" "-o" "-p" "-r" "-s" "-t" "-u" "-value-to-list" "-when-let" "-zip" "dash-"))
;;; End of scraped data
(provide 'dash-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dash-autoloads.el ends here

View file

@ -0,0 +1,14 @@
(define-package "dash" "20240103.1301" "A modern list library for Emacs"
'((emacs "24"))
:commit "e32a70ca636bad42232b6c79f1491dc86802a721" :authors
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainers
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainer
'("Magnar Sveen" . "magnars@gmail.com")
:keywords
'("extensions" "lisp")
:url "https://github.com/magnars/dash.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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.

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
* Dash: (dash.info). A modern list library for GNU Emacs.

View file

@ -0,0 +1,83 @@
;;; dash-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dash.el
(autoload 'dash-fontify-mode "dash" "\
Toggle fontification of Dash special variables.
Dash-Fontify mode is a buffer-local minor mode intended for Emacs
Lisp buffers. Enabling it causes the special variables bound in
anaphoric Dash macros to be fontified. These anaphoras include
`it', `it-index', `acc', and `other'. In older Emacs versions
which do not dynamically detect macros, Dash-Fontify mode
additionally fontifies Dash macro calls.
See also `dash-fontify-mode-lighter' and
`global-dash-fontify-mode'.
This is a minor mode. If called interactively, toggle the
`Dash-Fontify mode' mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `dash-fontify-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)" t)
(put 'global-dash-fontify-mode 'globalized-minor-mode t)
(defvar global-dash-fontify-mode nil "\
Non-nil if Global Dash-Fontify mode is enabled.
See the `global-dash-fontify-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `global-dash-fontify-mode'.")
(custom-autoload 'global-dash-fontify-mode "dash" nil)
(autoload 'global-dash-fontify-mode "dash" "\
Toggle Dash-Fontify mode in all buffers.
With prefix ARG, enable Global Dash-Fontify mode if ARG is positive;
otherwise, disable it.
If called from Lisp, toggle the mode if ARG is `toggle'.
Enable the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
Dash-Fontify mode is enabled in all buffers where
`dash--turn-on-fontify-mode' would do it.
See `dash-fontify-mode' for more information on Dash-Fontify mode.
(fn &optional ARG)" t)
(autoload 'dash-register-info-lookup "dash" "\
Register the Dash Info manual with `info-lookup-symbol'.
This allows Dash symbols to be looked up with \\[info-lookup-symbol]." t)
(register-definition-prefixes "dash" '("!cdr" "!cons" "--" "->" "-a" "-butlast" "-c" "-d" "-e" "-f" "-gr" "-i" "-juxt" "-keep" "-l" "-m" "-no" "-o" "-p" "-r" "-s" "-t" "-u" "-value-to-list" "-when-let" "-zip" "dash-"))
;;; End of scraped data
(provide 'dash-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dash-autoloads.el ends here

View file

@ -0,0 +1,14 @@
(define-package "dash" "20240510.1327" "A modern list library for Emacs"
'((emacs "24"))
:commit "1de9dcb83eacfb162b6d9a118a4770b1281bcd84" :authors
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainers
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainer
'("Magnar Sveen" . "magnars@gmail.com")
:keywords
'("extensions" "lisp")
:url "https://github.com/magnars/dash.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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.

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
* Dash: (dash.info). A modern list library for GNU Emacs.

View file

@ -0,0 +1,8 @@
######## ## ## ### ###### ######
## ### ### ## ## ## ## ## ##
## #### #### ## ## ## ##
###### ## ### ## ## ## ## ######
## ## ## ######### ## ##
## ## ## ## ## ## ## ## ##
######## ## ## ## ## ###### ######

View file

@ -0,0 +1,6 @@
_______ .___ ___. ___ ______ _______.
| ____|| \/ | / \ / | / |
| |__ | \ / | / ^ \ | ,----' | (----`
| __| | |\/| | / /_\ \ | | \ \
| |____ | | | | / _____ \ | `----.----) |
|_______||__| |__| /__/ \__\ \______|_______/

View file

@ -0,0 +1,8 @@
_______ _____ ______ ________ ________ ________
|\ ___ \ |\ _ \ _ \|\ __ \|\ ____\|\ ____\
\ \ __/|\ \ \\\__\ \ \ \ \|\ \ \ \___|\ \ \___|_
\ \ \_|/_\ \ \\|__| \ \ \ __ \ \ \ \ \_____ \
\ \ \_|\ \ \ \ \ \ \ \ \ \ \ \ \____\|____|\ \
\ \_______\ \__\ \ \__\ \__\ \__\ \_______\____\_\ \
\|_______|\|__| \|__|\|__|\|__|\|_______|\_________\
\|_________|

View file

@ -0,0 +1,17 @@
_ ___ _ _
_ ___ __ ___ __ _ ___
__ _ ___ __ ___
_ ___ _
_ _ __ _
___ __ _
__ _
_ _ _
_ _ _
_ _ _
__ ___
_ _ _ _
_ _
_ _
_ _
_
__

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -0,0 +1,39 @@
;;; dashboard-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dashboard.el
(autoload 'dashboard-open "dashboard" "\
Open (or refresh) the *dashboard* buffer.
(fn &rest _)" t)
(autoload 'dashboard-setup-startup-hook "dashboard" "\
Setup post initialization hooks unless a command line argument is provided.")
(register-definition-prefixes "dashboard" '("dashboard-"))
;;; Generated autoloads from dashboard-widgets.el
(register-definition-prefixes "dashboard-widgets" '("dashboard-" "recentf-list"))
;;; End of scraped data
(provide 'dashboard-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dashboard-autoloads.el ends here

View file

@ -0,0 +1,14 @@
(define-package "dashboard" "20240319.915" "A startup screen extracted from Spacemacs"
'((emacs "26.1"))
:commit "e34ce1b4730fb88a33a571b9065114b7a77645f0" :authors
'(("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com"))
:maintainers
'(("Jesús Martínez" . "jesusmartinez93@gmail.com"))
:maintainer
'("Jesús Martínez" . "jesusmartinez93@gmail.com")
:keywords
'("startup" "screen" "tools" "dashboard")
:url "https://github.com/emacs-dashboard/emacs-dashboard")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,556 @@
;;; dashboard.el --- A startup screen extracted from Spacemacs -*- lexical-binding: t -*-
;; Copyright (c) 2016-2024 emacs-dashboard maintainers
;;
;; Author : Rakan Al-Hneiti <rakan.alhneiti@gmail.com>
;; Maintainer : Jesús Martínez <jesusmartinez93@gmail.com>
;; Shen, Jen-Chieh <jcs090218@gmail.com>
;; URL : https://github.com/emacs-dashboard/emacs-dashboard
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3
;;
;; Created: October 05, 2016
;; Package-Version: 1.9.0-SNAPSHOT
;; Keywords: startup, screen, tools, dashboard
;; Package-Requires: ((emacs "26.1"))
;;; Commentary:
;; An extensible Emacs dashboard, with sections for
;; bookmarks, projects (projectile or project.el), org-agenda and more.
;;; Code:
(require 'ffap)
(require 'recentf)
(require 'dashboard-widgets)
;;
;;; Externals
(declare-function bookmark-get-filename "ext:bookmark.el")
(declare-function bookmark-all-names "ext:bookmark.el")
(declare-function dashboard-ls--dirs "ext:dashboard-ls.el")
(declare-function dashboard-ls--files "ext:dashboard-ls.el")
(declare-function page-break-lines-mode "ext:page-break-lines.el")
(declare-function projectile-remove-known-project "ext:projectile.el")
(declare-function project-forget-projects-under "ext:project.el")
(declare-function linum-mode "linum.el")
(declare-function dashboard-refresh-buffer "dashboard.el")
;;
;;; Customization
(defgroup dashboard nil
"Extensible startup screen."
:group 'applications)
;; Custom splash screen
(defvar dashboard-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-p") #'dashboard-previous-line)
(define-key map (kbd "C-n") #'dashboard-next-line)
(define-key map (kbd "<up>") #'dashboard-previous-line)
(define-key map (kbd "<down>") #'dashboard-next-line)
(define-key map (kbd "k") #'dashboard-previous-line)
(define-key map (kbd "j") #'dashboard-next-line)
(define-key map [tab] #'widget-forward)
(define-key map (kbd "C-i") #'widget-forward)
(define-key map [backtab] #'widget-backward)
(define-key map (kbd "RET") #'dashboard-return)
(define-key map [mouse-1] #'dashboard-mouse-1)
(define-key map (kbd "}") #'dashboard-next-section)
(define-key map (kbd "{") #'dashboard-previous-section)
(define-key map (kbd "<backspace>") #'dashboard-remove-item-under)
(define-key map (kbd "<delete>") #'dashboard-remove-item-under)
(define-key map (kbd "DEL") #'dashboard-remove-item-under)
(define-key map (kbd "1") #'dashboard-section-1)
(define-key map (kbd "2") #'dashboard-section-2)
(define-key map (kbd "3") #'dashboard-section-3)
(define-key map (kbd "4") #'dashboard-section-4)
(define-key map (kbd "5") #'dashboard-section-5)
(define-key map (kbd "6") #'dashboard-section-6)
(define-key map (kbd "7") #'dashboard-section-7)
(define-key map (kbd "8") #'dashboard-section-8)
(define-key map (kbd "9") #'dashboard-section-9)
map)
"Keymap for dashboard mode.")
(defcustom dashboard-before-initialize-hook nil
"Hook that is run before dashboard buffer is initialized."
:group 'dashboard
:type 'hook)
(defcustom dashboard-after-initialize-hook nil
"Hook that is run after dashboard buffer is initialized."
:group 'dashboard
:type 'hook)
(defcustom dashboard-hide-cursor nil
"Whether to hide the cursor in the dashboard."
:type 'boolean
:group 'dashboard)
(define-derived-mode dashboard-mode special-mode "Dashboard"
"Dashboard major mode for startup screen."
:group 'dashboard
:syntax-table nil
:abbrev-table nil
(buffer-disable-undo)
(when (featurep 'whitespace) (whitespace-mode -1))
(when (featurep 'linum) (linum-mode -1))
(when (featurep 'display-line-numbers) (display-line-numbers-mode -1))
(when (featurep 'page-break-lines) (page-break-lines-mode 1))
(setq-local revert-buffer-function #'dashboard-refresh-buffer)
(when dashboard-hide-cursor
(setq-local cursor-type nil))
(setq inhibit-startup-screen t
buffer-read-only t
truncate-lines t))
(defcustom dashboard-center-content nil
"Whether to center content within the window."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-vertically-center-content nil
"Whether to vertically center content within the window."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-startupify-list
'(dashboard-insert-banner
dashboard-insert-newline
dashboard-insert-banner-title
dashboard-insert-newline
dashboard-insert-init-info
dashboard-insert-items
dashboard-insert-newline
dashboard-insert-footer)
"List of dashboard widgets (in order) to insert in dashboard buffer.
Avalaible functions:
`dashboard-insert-newline'
`dashboard-insert-page-break'
`dashboard-insert-banner'
`dashboard-insert-banner-title'
`dashboard-insert-navigator'
`dashboard-insert-init-info'
`dashboard-insert-items'
`dashboard-insert-footer'
You can also add your custom function or a lambda to the list.
example:
(lambda () (delete-char -1))"
:type '(repeat function)
:group 'dashboard)
(defcustom dashboard-navigation-cycle nil
"Non-nil cycle the section navigation."
:type 'boolean
:group 'dashboard)
(defconst dashboard-buffer-name "*dashboard*"
"Dashboard's buffer name.")
(defvar dashboard-force-refresh nil
"If non-nil, force refresh dashboard buffer.")
(defvar dashboard--section-starts nil
"List of section starting positions.")
;;
;;; Util
(defun dashboard--goto-line (line)
"Goto LINE."
(goto-char (point-min)) (forward-line (1- line)))
(defmacro dashboard--save-excursion (&rest body)
"Execute BODY save window point."
(declare (indent 0) (debug t))
`(let ((line (line-number-at-pos nil t))
(column (current-column)))
,@body
(dashboard--goto-line line)
(move-to-column column)))
;;
;;; Core
(defun dashboard--separator ()
"Return separator used to search."
(concat "\n" dashboard-page-separator))
(defun dashboard--current-section ()
"Return section symbol in dashboard."
(save-excursion
(if-let* ((sep (dashboard--separator))
((and (search-backward sep nil t)
(search-forward sep nil t))))
(let ((ln (thing-at-point 'line)))
(cond ((string-match-p "Recent Files:" ln) 'recents)
((string-match-p "Bookmarks:" ln) 'bookmarks)
((string-match-p "Projects:" ln) 'projects)
((string-match-p "Agenda for " ln) 'agenda)
((string-match-p "Registers:" ln) 'registers)
((string-match-p "List Directories:" ln) 'ls-directories)
((string-match-p "List Files:" ln) 'ls-files)
(t (user-error "Unknown section from dashboard"))))
(user-error "Failed searching dashboard section"))))
;;
;;; Navigation
(defun dashboard-previous-section ()
"Navigate forward to next section."
(interactive)
(let* ((items-len (1- (length dashboard-items)))
(first-item (car (nth 0 dashboard-items)))
(current (or (ignore-errors (dashboard--current-section))
first-item))
(items (mapcar #'car dashboard-items))
(find (cl-position current items :test #'equal))
(prev-index (1- find))
(prev (cond (dashboard-navigation-cycle
(if (< prev-index 0) (nth items-len items)
(nth prev-index items)))
(t
(if (< prev-index 0) (nth 0 items)
(nth prev-index items))))))
(dashboard--goto-section prev)))
(defun dashboard-next-section ()
"Navigate forward to next section."
(interactive)
(let* ((items-len (1- (length dashboard-items)))
(last-item (car (nth items-len dashboard-items)))
(current (or (ignore-errors (dashboard--current-section))
last-item))
(items (mapcar #'car dashboard-items))
(find (cl-position current items :test #'equal))
(next-index (1+ find))
(next (cond (dashboard-navigation-cycle
(or (nth next-index items)
(nth 0 items)))
(t
(if (< items-len next-index)
(nth (min items-len next-index) items)
(nth next-index items))))))
(dashboard--goto-section next)))
(defun dashboard--section-lines ()
"Return a list of integer represent the starting line number of each section."
(let (pb-lst)
(save-excursion
(goto-char (point-min))
(while (search-forward (dashboard--separator) nil t)
(when (ignore-errors (dashboard--current-section))
(push (line-number-at-pos) pb-lst))))
(setq pb-lst (reverse pb-lst))
pb-lst))
(defun dashboard--goto-section-by-index (index)
"Navigate to item section by INDEX."
(let* ((pg-lst (dashboard--section-lines))
(items-id (1- index))
(items-pg (nth items-id pg-lst))
(items-len (length pg-lst)))
(when (and items-pg (< items-id items-len))
(dashboard--goto-line items-pg))))
(defun dashboard-section-1 ()
"Navigate to section 1." (interactive) (dashboard--goto-section-by-index 1))
(defun dashboard-section-2 ()
"Navigate to section 2." (interactive) (dashboard--goto-section-by-index 2))
(defun dashboard-section-3 ()
"Navigate to section 3." (interactive) (dashboard--goto-section-by-index 3))
(defun dashboard-section-4 ()
"Navigate to section 4." (interactive) (dashboard--goto-section-by-index 4))
(defun dashboard-section-5 ()
"Navigate to section 5." (interactive) (dashboard--goto-section-by-index 5))
(defun dashboard-section-6 ()
"Navigate to section 6." (interactive) (dashboard--goto-section-by-index 6))
(defun dashboard-section-7 ()
"Navigate to section 7." (interactive) (dashboard--goto-section-by-index 7))
(defun dashboard-section-8 ()
"Navigate to section 8." (interactive) (dashboard--goto-section-by-index 8))
(defun dashboard-section-9 ()
"Navigate to section 9." (interactive) (dashboard--goto-section-by-index 9))
(defun dashboard-previous-line (arg)
"Move point up and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
(interactive "^p")
(dashboard-next-line (- arg)))
(defun dashboard-next-line (arg)
"Move point down and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
;; code heavily inspired by `dired-next-line'
(interactive "^p")
(let (line-move-visual goal-column)
(line-move arg t))
;; We never want to move point into an invisible line. Dashboard doesnt
;; use invisible text currently but when it does were ready!
(while (and (invisible-p (point))
(not (if (and arg (< arg 0)) (bobp) (eobp))))
(forward-char (if (and arg (< arg 0)) -1 1)))
(beginning-of-line-text))
;;
;;; ffap
(defun dashboard--goto-section (section)
"Move to SECTION declares in variable `dashboard-item-shortcuts'."
(let ((fnc (intern (format "dashboard-jump-to-%s" section))))
(dashboard-funcall-fboundp fnc)))
(defun dashboard--current-index (section &optional pos)
"Return the idex by SECTION from POS."
(let (target-ln section-line)
(save-excursion
(when pos (goto-char pos))
(setq target-ln (line-number-at-pos))
(dashboard--goto-section section)
(setq section-line (line-number-at-pos)))
(- target-ln section-line)))
(defun dashboard--section-list (section)
"Return the list from SECTION."
(cl-case section
(`recents recentf-list)
(`bookmarks (bookmark-all-names))
(`projects (dashboard-projects-backend-load-projects))
(`ls-directories (dashboard-ls--dirs))
(`ls-files (dashboard-ls--files))
(t (user-error "Unknown section for search: %s" section))))
(defun dashboard--current-item-in-path ()
"Return the path from current dashboard section in path."
(let ((section (dashboard--current-section)) path)
(cl-case section
(`bookmarks (setq path (bookmark-get-filename path)))
(t
(let ((lst (dashboard--section-list section))
(index (dashboard--current-index section)))
(setq path (nth index lst)))))
path))
(defun dashboard--on-path-item-p ()
"Return non-nil if current point is on the item path from dashboard."
(save-excursion
(when (= (point) (line-end-position)) (ignore-errors (forward-char -1)))
(eq (get-char-property (point) 'face) 'dashboard-items-face)))
(defun dashboard--ffap-guesser--adv (fnc &rest args)
"Advice execution around function `ffap-guesser'.
Argument FNC is the adviced function.
Optional argument ARGS adviced function arguments."
(cl-case major-mode
(`dashboard-mode
(or (and (dashboard--on-path-item-p)
(dashboard--current-item-in-path))
(apply fnc args))) ; fallback
(t (apply fnc args))))
(advice-add 'ffap-guesser :around #'dashboard--ffap-guesser--adv)
;;
;;; Removal
(defun dashboard-remove-item-under ()
"Remove a item from the current item section."
(interactive)
(cl-case (dashboard--current-section)
(`recents (dashboard-remove-item-recentf))
(`bookmarks (dashboard-remove-item-bookmarks))
(`projects (dashboard-remove-item-projects))
(`agenda (dashboard-remove-item-agenda))
(`registers (dashboard-remove-item-registers)))
(dashboard--save-excursion (dashboard-refresh-buffer)))
(defun dashboard-remove-item-recentf ()
"Remove a file from `recentf-list'."
(interactive)
(let ((path (save-excursion (end-of-line) (ffap-guesser))))
(setq recentf-list (delete path recentf-list)))
(dashboard-mute-apply (recentf-save-list)))
(defun dashboard-remove-item-projects ()
"Remove a path from `project--list'."
(interactive)
(let ((path (save-excursion (end-of-line) (ffap-guesser))))
(dashboard-mute-apply
(cl-case dashboard-projects-backend
(`projectile (projectile-remove-known-project path))
(`project-el (project-forget-projects-under path))))))
(defun dashboard-remove-item-bookmarks ()
"Remove a bookmarks from `bookmark-alist'."
(interactive)) ; TODO: ..
(defun dashboard-remove-item-agenda ()
"Remove an agenda from `org-agenda-files'."
(interactive "P")
(let ((agenda-file (get-text-property (point) 'dashboard-agenda-file))
(agenda-loc (get-text-property (point) 'dashboard-agenda-loc)))
(with-current-buffer (find-file-noselect agenda-file)
(goto-char agenda-loc)
(call-interactively 'org-todo))))
(defun dashboard-remove-item-registers ()
"Remove a registers from `register-alist'."
(interactive)) ; TODO: ..
;;
;;; Confirmation
(defun dashboard-return ()
"Hit return key in dashboard buffer."
(interactive)
(let ((start-ln (line-number-at-pos)) (fd-cnt 0) diff-line entry-pt)
(save-excursion
(while (and (not diff-line)
(not (= (point) (point-min)))
(not (get-char-property (point) 'button))
(not (= (point) (point-max))))
(forward-char 1)
(setq fd-cnt (1+ fd-cnt))
(unless (= start-ln (line-number-at-pos))
(setq diff-line t)))
(unless (= (point) (point-max))
(setq entry-pt (point))))
(when (= fd-cnt 1)
(setq entry-pt (1- (point))))
(if entry-pt
(widget-button-press entry-pt)
(call-interactively #'widget-button-press))))
(defun dashboard-mouse-1 ()
"Key for keymap `mouse-1'."
(interactive)
(let ((old-track-mouse track-mouse))
(when (call-interactively #'widget-button-click)
(setq track-mouse old-track-mouse))))
;;
;;; Insertion
(defmacro dashboard--with-buffer (&rest body)
"Execute BODY in dashboard buffer."
(declare (indent 0))
`(with-current-buffer (get-buffer-create dashboard-buffer-name)
(let ((inhibit-read-only t)) ,@body)
(current-buffer)))
(defun dashboard-insert-items ()
"Function to insert dashboard items.
See `dashboard-item-generators' for all items available."
(let ((recentf-is-on (recentf-enabled-p))
(origial-recentf-list recentf-list))
(mapc (lambda (els)
(let* ((el (or (car-safe els) els))
(list-size
(or (cdr-safe els)
dashboard-items-default-length))
(item-generator
(cdr-safe (assoc el dashboard-item-generators))))
(insert "\n")
(push (point) dashboard--section-starts)
(funcall item-generator list-size)
(goto-char (point-max))
(when recentf-is-on
(setq recentf-list origial-recentf-list))))
dashboard-items)
(when dashboard-center-content
(dashboard-center-text
(if dashboard--section-starts
(car (last dashboard--section-starts))
(point))
(point-max)))
(save-excursion
(dolist (start dashboard--section-starts)
(goto-char start)
(insert dashboard-page-separator)))
(insert "\n")
(insert dashboard-page-separator)))
(defun dashboard-insert-startupify-lists ()
"Insert the list of widgets into the buffer."
(interactive)
(let ((inhibit-redisplay t)
(recentf-is-on (recentf-enabled-p))
(origial-recentf-list recentf-list)
(dashboard-num-recents (or (cdr (assoc 'recents dashboard-items)) 0)))
(when recentf-is-on
(setq recentf-list (dashboard-subseq recentf-list dashboard-num-recents)))
(dashboard--with-buffer
(when (or dashboard-force-refresh (not (eq major-mode 'dashboard-mode)))
(run-hooks 'dashboard-before-initialize-hook)
(erase-buffer)
(setq dashboard--section-starts nil)
(mapc (lambda (fn)
(funcall fn))
dashboard-startupify-list)
(when dashboard-vertically-center-content
(goto-char (point-min))
(when-let* ((content-height (cdr (window-absolute-pixel-position (point-max))))
(vertical-padding (floor (/ (- (window-pixel-height) content-height) 2)))
((> vertical-padding 0))
(vertical-lines (1- (floor (/ vertical-padding (line-pixel-height)))))
((> vertical-lines 0)))
(insert (make-string vertical-lines ?\n))))
(goto-char (point-min))
(dashboard-mode)))
(when recentf-is-on
(setq recentf-list origial-recentf-list))))
;;;###autoload
(defun dashboard-open (&rest _)
"Open (or refresh) the *dashboard* buffer."
(interactive)
(let ((dashboard-force-refresh t)) (dashboard-insert-startupify-lists))
(switch-to-buffer dashboard-buffer-name))
(defalias #'dashboard-refresh-buffer #'dashboard-open)
(defun dashboard-resize-on-hook (&optional _)
"Re-render dashboard on window size change."
(let ((space-win (get-buffer-window dashboard-buffer-name))
(frame-win (frame-selected-window)))
(when (and space-win
(not (window-minibuffer-p frame-win)))
(with-selected-window space-win
(dashboard-insert-startupify-lists)))))
(defun dashboard-initialize ()
"Switch to dashboard and run `dashboard-after-initialize-hook'."
(switch-to-buffer dashboard-buffer-name)
(goto-char (point-min))
(redisplay)
(run-hooks 'dashboard-after-initialize-hook))
;;;###autoload
(defun dashboard-setup-startup-hook ()
"Setup post initialization hooks unless a command line argument is provided."
(when (< (length command-line-args) 2) ;; Assume no file name passed
(add-hook 'window-size-change-functions #'dashboard-resize-on-hook 100)
(add-hook 'window-setup-hook #'dashboard-resize-on-hook)
(add-hook 'after-init-hook #'dashboard-insert-startupify-lists)
(add-hook 'emacs-startup-hook #'dashboard-initialize)))
(provide 'dashboard)
;;; dashboard.el ends here

View file

@ -0,0 +1,8 @@
######## ## ## ### ###### ######
## ### ### ## ## ## ## ## ##
## #### #### ## ## ## ##
###### ## ### ## ## ## ## ######
## ## ## ######### ## ##
## ## ## ## ## ## ## ## ##
######## ## ## ## ## ###### ######

View file

@ -0,0 +1,6 @@
_______ .___ ___. ___ ______ _______.
| ____|| \/ | / \ / | / |
| |__ | \ / | / ^ \ | ,----' | (----`
| __| | |\/| | / /_\ \ | | \ \
| |____ | | | | / _____ \ | `----.----) |
|_______||__| |__| /__/ \__\ \______|_______/

View file

@ -0,0 +1,8 @@
_______ _____ ______ ________ ________ ________
|\ ___ \ |\ _ \ _ \|\ __ \|\ ____\|\ ____\
\ \ __/|\ \ \\\__\ \ \ \ \|\ \ \ \___|\ \ \___|_
\ \ \_|/_\ \ \\|__| \ \ \ __ \ \ \ \ \_____ \
\ \ \_|\ \ \ \ \ \ \ \ \ \ \ \ \____\|____|\ \
\ \_______\ \__\ \ \__\ \__\ \__\ \_______\____\_\ \
\|_______|\|__| \|__|\|__|\|__|\|_______|\_________\
\|_________|

View file

@ -0,0 +1,17 @@
_ ___ _ _
_ ___ __ ___ __ _ ___
__ _ ___ __ ___
_ ___ _
_ _ __ _
___ __ _
__ _
_ _ _
_ _ _
_ _ _
__ ___
_ _ _ _
_ _
_ _
_ _
_
__

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -0,0 +1,39 @@
;;; dashboard-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dashboard.el
(autoload 'dashboard-open "dashboard" "\
Open (or refresh) the *dashboard* buffer.
(fn &rest _)" t)
(autoload 'dashboard-setup-startup-hook "dashboard" "\
Setup post initialization hooks unless a command line argument is provided.")
(register-definition-prefixes "dashboard" '("dashboard-"))
;;; Generated autoloads from dashboard-widgets.el
(register-definition-prefixes "dashboard-widgets" '("dashboard-" "recentf-list"))
;;; End of scraped data
(provide 'dashboard-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dashboard-autoloads.el ends here

View file

@ -0,0 +1,15 @@
(define-package "dashboard" "20240529.2058" "A startup screen extracted from Spacemacs"
'((emacs "26.1"))
:commit "3852301f9c6f3104d9cc98389612b5ef3452a7de" :authors
'(("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com"))
:maintainers
'(("Jesús Martínez" . "jesusmartinez93@gmail.com")
("Jen-Chieh" . "jcs090218@gmail.com"))
:maintainer
'("Jesús Martínez" . "jesusmartinez93@gmail.com")
:keywords
'("startup" "screen" "tools" "dashboard")
:url "https://github.com/emacs-dashboard/emacs-dashboard")
;; Local Variables:
;; no-byte-compile: t
;; End:

Some files were not shown because too many files have changed in this diff Show more