From 374ae3de24187512adddf01a56e5eb52c79db65f Mon Sep 17 00:00:00 2001 From: Blendoit Date: Sat, 1 Aug 2020 15:18:40 -0700 Subject: Include contents of elpa/ sources + theme update. --- elpa/mode-icons-20190627.2121/mode-icons.el | 1883 +++++++++++++++++++++++++++ 1 file changed, 1883 insertions(+) create mode 100644 elpa/mode-icons-20190627.2121/mode-icons.el (limited to 'elpa/mode-icons-20190627.2121/mode-icons.el') diff --git a/elpa/mode-icons-20190627.2121/mode-icons.el b/elpa/mode-icons-20190627.2121/mode-icons.el new file mode 100644 index 0000000..cfa68ec --- /dev/null +++ b/elpa/mode-icons-20190627.2121/mode-icons.el @@ -0,0 +1,1883 @@ +;;; mode-icons.el --- Show icons for modes -*- lexical-binding: t; -*- + +;; Copyright (C) 2013, 2016 Tom Willemse +;; 2016 Matthew L. Fidler + +;; Author: Tom Willemse +;; Keywords: multimedia +;; Version: 0.4.0 +;; URL: http://ryuslash.org/projects/mode-icons.html +;; Package-Requires: ((emacs "24") (cl-lib "0.5")) + +;; 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 . + +;;; Commentary: + +;; This package provides a globalized minor mode that replaces the +;; major mode name in your mode-line and places like Ibuffer with an +;; icon. Currently the following programming modes are supported, +;; among others: +;; +;; - C +;; - C++ +;; - C# +;; - CSS +;; - Coffee +;; - Dart +;; - Emacs-Lisp +;; - HTML +;; - Haml +;; - JavaScript +;; - Lisp +;; - Lua +;; - nXML +;; - PHP +;; - Python +;; - React +;; - Ruby +;; - Rust +;; - Sass/Scss +;; - Scheme +;; - Shell-script +;; - Slim +;; - Snippet +;; - Web +;; - Yaml +;; +;; To enable this minor mode add the following line to your init file: +;; +;; (mode-icons-mode) +;; +;; As of version 0.3.0 this project includes some icons which can use icon +;; fonts instead of images. These fonts are: +;; +;; - Font Awesome, found at URL `http://fontawesome.io/'. +;; - GitHub Octicons, found at URL `https://octicons.github.com/'. +;; - Font Mfizz, found at URL `http://fizzed.com/oss/font-mfizz'. +;; - IcoMoon, found at URL `https://icomoon.io/#icons-icomoon'. +;; + +;;; Code: + +(declare-function comint-send-string "comint") +(declare-function emojify-set-emoji-data "emojify") +(declare-function ht-get "ht") +(declare-function powerline-minor-modes "powerline") +(declare-function powerline-raw "powerline-raw") +(declare-function pl/add-text-property "powerline") +(declare-function mode-icons--real-powerline-raw "powerline") +(declare-function mode-icons--powerline-raw "mode-icons") +(declare-function mode-icons--real-powerline-major-mode "powerline") +(declare-function mode-icons--powerline-major-mode "mode-icons") + +(require 'cl-lib) +(require 'color) +(require 'emojify nil t) + +(defgroup mode-icons nil + "Provide icons for major modes." + :group 'editing-basics + :group 'convenience) + +(defconst mode-icons--directory + (if load-file-name + (file-name-directory load-file-name) + default-directory) + "Where mode-icons was loaded from.") + +(defun mode-icons-get-icon-file (icon) + "Get the location of ICON. + +ICON should be a file name with extension. The result is the +absolute path to ICON." + (expand-file-name icon (expand-file-name "icons" mode-icons--directory))) + +(defmacro mode-icons-save-buffer-state (&rest body) + "Eval BODY saving buffer state. +This macro restores the buffer state under the assumption that no +significant modification has been made in BODY. A change is +considered significant if it affects the buffer text in any way +that isn't completely restored again. Changes in text properties +like `face' or `syntax-table' are considered insignificant. This +macro allows text properties to be changed, even in a read-only +buffer. + +This macro should be placed around all calculations which set +\"insignificant\" text properties in a buffer, even when the buffer is +known to be writeable. That way, these text properties remain set +even if the user undoes the command which set them. + +This macro should ALWAYS be placed around \"temporary\" internal buffer +changes \(like adding a newline to calculate a text-property then +deleting it again\), so that the user never sees them on his +`buffer-undo-list'. + +However, any user-visible changes to the buffer \(like auto-newlines\) +must not be within a `ergoemacs-save-buffer-state', since the user then +wouldn't be able to undo them. + +The return value is the value of the last form in BODY. + +This was stole/modified from `c-save-buffer-state'" + `(let* ((modified (buffer-modified-p)) (buffer-undo-list t) + (inhibit-read-only t) (inhibit-point-motion-hooks t) + before-change-functions after-change-functions + deactivate-mark + buffer-file-name buffer-file-truename ; Prevent primitives checking + ; for file modification + ) + (unwind-protect + (progn ,@body) + (and (not modified) + (buffer-modified-p) + (set-buffer-modified-p nil))))) + +(defcustom mode-icons + `(("\\`CSS\\'" "css" xpm) + ("\\`Coffee\\'" "coffee" xpm-bw) + ("\\`Compilation\\'" "compile" xpm) + ("\\`Dart\\'" "dart" xpm) + ("\\`Flutter\\'" "flutter" xpm) + ("\\`Elixir\\'" "elixir" xpm) + ("\\`Erlang\\'" "erlang" xpm) + ("\\`Emacs-Lisp\\'" "emacs" xpm) + ("\\`Lisp Interaction\\'" "emacs" xpm) + ("\\`HTML\\'" "html" xpm) + ("\\`Haml\\'" "haml" xpm) + ("\\`Image\\[imagemagick\\]\\'" "svg" xpm) + ("\\`Inf-Ruby\\'" "infruby" xpm) + ("\\`Java[Ss]cript\\'" "js" xpm) + ("\\`Lisp\\'" "cl" xpm) + ("\\`Lua\\'" "Lua-Logo_16x16" png) + ("\\`nXML\\'" "xml" xpm) + ("\\`Org\\'" "org" xpm) + ("\\`PHP\\(\\|/.*\\)\\'" "php" xpm) + ("\\`Projectile Rails Server\\'" "rails" xpm) + ("\\`Python\\'" "python" xpm) + ("\\` Emmet\\'" "emmet" xpm) + ("\\`RJSX\\'" "react" xpm) + ("\\`Ruby\\'" "ruby" xpm) + ("\\`Rust\\'" "rust" xpm) + ("\\`EnhRuby\\'" "ruby" xpm) + ("\\`ESS\\[S\\]\\'" "R" xpm) + ("\\`ESS\\[SAS\\]\\'" "sas" xpm) + ("\\`ESS\\[BUGS\\]\\'" #xf188 FontAwesome) + ("\\`iESS\\'" "R" xpm) + ("\\`SCSS\\'" "sass" xpm) + ("\\`Sass\\'" "sass" xpm) + ("\\`Scheme" "scheme" xpm-bw) + ("\\`Shell-script" "bash" xpm-bw) + ("\\`Slim" "slim" xpm-bw) + ("\\`Snippet" "yas" xpm) + ("\\`Term\\'" "term" xpm) + ("\\`Web\\'" "html" xpm) + ("\\`XML\\'" "xml" xpm) + ("\\`YAML\\'" "yaml" xpm) + ("\\` ?YASnippet\\'" "yas" xpm) + ("\\` ?yas\\'" "yas" xpm) + ("\\` ?hs\\'" "hs" xpm) + ("\\`Markdown\\'" #xf0c9 github-octicons) + ("\\`GFM\\'" #xf0c9 github-octicons) + ("\\`Scala\\'" #xf15b font-mfizz) + ("\\`Magit\\'" #xf1d2 FontAwesome) + ("\\` Pulls\\'" #xf092 FontAwesome) + ("\\`Zip-Archive\\'" #xf1c6 FontAwesome) + ("\\` ARev\\'" #xf021 FontAwesome) + ("\\`Calc\\(ulator\\)?\\'" #xf1ec FontAwesome) + ("\\`Debug.*\\'" #xf188 FontAwesome) + ("\\`Debug.*\\'" #xf188 FontAwesome) + ("\\`Calendar\\'" #xf073 FontAwesome) + ("\\`Help\\'" #xf059 FontAwesome) + ("\\`WoMan\\'" #xf05a FontAwesome) + ("\\`C\\(/.*\\|\\)\\'" "c" xpm) + ("\\`Custom\\'" #xf013 FontAwesome) + ("\\`Go\\'" "go" xpm) + ("\\` ?Rbow\\'" "rainbow" xpm) + ("\\` ?ivy\\'" "ivy" xpm) ;; Icon created by Philipp Lehmann from the Noun Project https://thenounproject.com/search/?q=ivy&i=329756 + ("\\` ?ICY\\'" "icy" xpm) ;; http://www.clipartpal.com/clipart_pd/weather/ice_10206.html + ("\\` ?Golden\\'" "golden" xpm-bw) ;; Icon created by Arthur Shlain from Noun Project + ("\\`BibTeX\\'\\'" "bibtex" xpm-bw) + ("\\`C[+][+]\\(/.*\\|\\)\\'" "cpp" xpm) + ("\\`C[#]\\(/.*\\|\\)\\'" "csharp" xpm) + ("\\`Haskell\\'" #xf126 font-mfizz) + ("\\`Clojure\\'" #xf10b font-mfizz) + ("\\`Java\\(/.*\\|\\)\\'" #xf12b font-mfizz) + ("\\`C?Perl\\'" #xf148 font-mfizz) + ("\\`Octave\\'" "octave" xpm) + ("\\`AHK\\'" "autohotkey" xpm) + ("\\`Info\\'" #xf05a FontAwesome) + ("\\` ?Narrow\\'" #xf066 FontAwesome) + ("\\`Dockerfile\\'" "docker" xpm) + ("\\`Spacemacs buffer\\'" "spacemacs" png) + ("\\` ?emoji\\'" "emoji" png) + ("\\`Org-Agenda" #xf046 FontAwesome) + ("\\`PS\\'" "powershell" xpm) + (mode-icons-powershell-p "powershell" xpm) + (mode-icons-cmd-p "cmd" xpm-bw) + (mode-icons-msys-p "msys" xpm) + (mode-icons-cygwin-p "cygwin" xpm) + (read-only #xf023 FontAwesome) + (writable #xf09c FontAwesome) + (save #xf0c7 FontAwesome) + (saved "" nil) + (modified-outside #xf071 FontAwesome) + (steal #xf21b FontAwesome) + ;; Prefer finder icon since it looks like the old mac icon + (apple #xeabf IcoMoon-Free) + (apple #xf179 FontAwesome) + (win #xf17a FontAwesome) + ;; FIXME: use lsb_release to determine Linux variant and choose appropriate icon + (unix #xeabd IcoMoon-Free) ;; Clear Tux (Unlike FontAwesome) + + ;; This icon is clearer than FontAwesome's Linux Penguin + (unix #xf166 font-mfizz) ;; Use ubuntu, since I think it is the most common. + (unix #xf17c FontAwesome) ;; Fall Back to FontAwesome + (undecided #xf128 FontAwesome) + ("Text\\'" #xf0f6 FontAwesome) + ("\\` ?company\\'" #xf1ad FontAwesome) + ("\\` ?AC\\'" #xf18e FontAwesome) + ("\\` ?Fly\\'" #xea12 IcoMoon-Free) + ;; ("\\` ?FlyC.*\\'" "flycheck" xpm) + ("\\` ?SP\\(/s\\)?\\'" "smartparens" xpm) + ("\\` ?Ergo" #xf11c FontAwesome) + ("\\` ?drag\\'" #xf047 FontAwesome) + ("\\` ?Helm\\'" "helm" xpm-bw) ;; By Noe Araujo, MX, https://thenounproject.com/term/helm/233101/ + ("\\`Messages\\'" #xf27b FontAwesome) + ("\\`Conf" #xf1de FontAwesome) + ("\\`Fundamental\\'" #xf016 FontAwesome) + ("\\`Javascript-IDE\\'" "js" xpm) + ("\\` Undo-Tree\\'" ":palm_tree:" emoji) + ("\\`LaTeX\\'" "tex" ext) + ("\\`Image\\[xpm\\]\\'" "xpm" ext) + ("\\`Image\\[png\\]\\'" "png" ext) + ("\\` ?AI\\'" #xf03c FontAwesome) + ("\\` ?Isearch\\'" #xf002) + (default #xf059 FontAwesome) + ;; Diminished modes + ("\\` ?\\(?:ElDoc\\|Anzu\\|SP\\|Guide\\|PgLn\\|Undo-Tree\\|Ergo.*\\|,\\|Isearch\\|Ind\\)\\'" nil nil)) + "Icons for major and minor modes. + +Each specificatioun is a list with the first element being the +name of the major mode. The second the name of the icon file, +without the extension. And the third being the type of icon." + :type '(repeat + (list (choice + (string :tag "Regular Expression") + (const :tag "Read Only Indicator" read-only) + (const :tag "Writable Indicator" writable) + (const :tag "Saved" saved) + (const :tag "Save" save) + (const :tag "Modified Outside Emacs" modified-outside) + (const :tag "Locked By Someone Else" steal) + (const :tag "Apple" apple) + (const :tag "Windows" win) + (const :tag "Unix" unix) + (const :tag "Default Icon" default) + (function :tag "Enriched minor mode")) + (choice + (string :tag "Icon Name") + (integer :tag "Font Glyph Code") + (const :tag "ess" nil)) + (choice + (const :tag "text" nil) + (const :tag "png" png) + (const :tag "gif" gif) + (const :tag "jpeg" jpeg) + (const :tag "jpg" jpg) + (const :tag "xbm" xbm) + (const :tag "xpm" xpm) + (const :tag "Black and White xpm that changes color to match the mode-line face" xpm-bw) + (const :tag "Emoji" emoji) + (const :tag "Mode Icons Generated file-type" ext) + (symbol :tag "Font")))) + :group 'mode-icons) + +(defun mode-icons-powershell-p (&optional match) + "Is the current mode a powershell process?" + (let ((proc (get-buffer-process (current-buffer)))) + (and proc (string-match-p (or match "powershell") (car (process-command proc)))))) + +(defun mode-icons-cmd-p () + "Is the current mode a CMD shell?" + (mode-icons-powershell-p "cmdproxy")) + +(defun mode-icons-cygwin-p () + "Is the current mode a CMD shell?" + (mode-icons-powershell-p "cygwin")) + +(defun mode-icons-msys-p () + "Is the current mode a CMD shell?" + (mode-icons-powershell-p "msys")) + +(defvar mode-icons-get-xpm-string (make-hash-table :test 'equal)) +(defun mode-icons-get-xpm-string (icon-path) + "Get XPM file contents for ICON-PATH. +If ICON-PATH is a string, return that." + (or (and (file-exists-p icon-path) + (or (gethash icon-path mode-icons-get-xpm-string) + (puthash icon-path (mode-icons-save-buffer-state (with-temp-buffer (insert-file-contents icon-path) (buffer-string))) + mode-icons-get-xpm-string))) + (and (stringp icon-path) icon-path))) + +(defun mode-icons-get-icon-display-xpm-replace (icon-path rep-alist &optional name) + "Get xpm image from ICON-PATH and replace REP-ALIST in file. +When NAME is non-nil, also replace the internal xpm image name." + (let ((case-fold-search t) + (img (mode-icons-get-xpm-string icon-path)) + (i 0)) + (dolist (c rep-alist) + (setq img (replace-regexp-in-string (regexp-quote (car c)) (format "COLOR<%d>" i) img t t) + i (1+ i))) + (let ((i 0)) + (dolist (c rep-alist) + (setq img (replace-regexp-in-string (format "COLOR<%d>" i) (cdr c) img t t) + i (1+ i)))) + (when name + (setq img (replace-regexp-in-string "^[ ]*static[ ]+char[ ]+[*][ ]+.*?\\[" (concat "static char * " name "[") img t t))) + img)) + +(defun mode-icons-interpolate (c1 c2 &optional factor) + "Interpolate between C1 and C2 by FACTOR. +If FACTOR is unspecified, use 0.5" + (let* ((factor (or factor 0.5)) + (red (+ (* (nth 0 c1) factor) (* (nth 0 c2) (- 1.0 factor)))) + (green (+ (* (nth 1 c1) factor) (* (nth 1 c2) (- 1.0 factor)))) + (blue (+ (* (nth 2 c1) factor) (* (nth 2 c2) (- 1.0 factor))))) + (setq red (/ (round (* 256.0 red)) 256.0) + green (/ (round (* 256.0 green)) 256.0) + blue (/ (round (* 256.0 blue)) 256.0)) + (color-rgb-to-hex red green blue))) + +(defun mode-icons-interpolate-from-scale (foreground background) + "Interpolate black to FOREGROUND and white to BACKGROUND. +Grayscales are in between. +Assumes that FOREGROUND and BACKGROUND are (r g b) lists." + (let ((black '(0.0 0.0 0.0)) + (white '(1.0 1.0 1.0)) + lst tmp + (i 0)) + (while (< i 256) + (setq tmp (/ i 255.0)) + (push (cons (upcase (mode-icons-interpolate black white tmp)) + (upcase (mode-icons-interpolate foreground background tmp))) lst) + (setq i (1+ i))) + lst)) + +(defvar mode-icons-get-icon-display-xpm-bw-face (make-hash-table) + "Hash table of dynamic images.") + +(defun mode-icons-background-color (&optional face) + "Get the background color of FACE. +In order, will try to get the background color from: +- FACE +- `mode-line' face +- `default' face +- Assume white." + (color-name-to-rgb (or (face-background (or face 'mode-line)) + (face-background 'mode-line) + (face-background 'default) + "white"))) + +(defun mode-icons-foreground-color (&optional face) + "Get the foreground color of FACE. +In order, will try to get the foreground color from: +- FACE +- `mode-line' face +- `default' face +- Assume black." + (color-name-to-rgb (or (face-foreground (or face 'mode-line)) + (face-foreground 'mode-line) + (face-foreground 'default) + "black"))) + +(defun mode-icons-get-icon-display-xpm-bw-face (icon-path &optional face) + "Change xpm at ICON-PATH to match FACE. +The white is changed to the background color. +The black is changed to the foreground color. +Grayscale colors are also changed by `mode-icons-interpolate-from-scale'." + (let* ((background (mode-icons-background-color face)) + (foreground (mode-icons-foreground-color face)) + (lst (mode-icons-interpolate-from-scale foreground background)) + (name (concat "mode_icons_bw_" (substring (mode-icons-interpolate background foreground 0.0) 1) "_" + (substring (mode-icons-interpolate background foreground 1.0) 1) "_" + (file-name-sans-extension (file-name-nondirectory icon-path)))) + (sym (intern name))) + (or (gethash sym mode-icons-get-icon-display-xpm-bw-face) + (puthash sym (mode-icons-get-icon-display-xpm-replace icon-path lst name) mode-icons-get-icon-display-xpm-bw-face)))) + +(defun mode-icons-get-xpm-icon-colors (icon-path) + "Get a list of rgb colors based on ICON-PATH xpm icon. +ICON-PATH can be a XPM string or a XPM file." + (let (colors) + (mode-icons-save-buffer-state + (with-temp-buffer + (insert (mode-icons-get-xpm-string icon-path)) + (goto-char (point-min)) + (while (re-search-forward "#[0-9A-Fa-f]\\{6\\}" nil t) + (push (color-name-to-rgb (match-string 0)) colors)))) + colors)) + +(defun mode-icons-desaturate-colors (colors &optional foreground background) + "Desaturate COLORS. + +If COLORS is an icon-path of an xpm file, use the colors from +that file. + +When FOREGROUND and BACKGROUND are both non-nil, use +`mode-icons-interpolate-from-scale' to change the grayscale to +match the foreground (black) and background (white) colors. + +Assume that COLORS is a list of (r g b) values. + +Returns a replacement list for `mode-icons-get-icon-display-xpm-replace'" + (if (and colors (stringp colors)) + (mode-icons-desaturate-colors (mode-icons-get-xpm-icon-colors colors) foreground background) + (let (color-list + val tmp + (trans-alist (and foreground background (mode-icons-interpolate-from-scale foreground background)))) + (dolist (color colors) + (setq val (+ (* 0.3 (nth 0 color)) (* 0.59 (nth 1 color)) (* 0.11 (nth 2 color))) + val (upcase (color-rgb-to-hex val val val))) + (when (and trans-alist (setq tmp (assoc val trans-alist))) + (setq val (cdr tmp))) + (push (cons (upcase (color-rgb-to-hex (nth 0 color) (nth 1 color) (nth 2 color))) val) color-list)) + color-list))) + +(defun mode-icons-desaturate-xpm (icon-path &optional face) + "Desaturate the xpm at ICON-PATH. +When FACE is non-nil, match the foreground and background colors +in FACE instead of making the image black and white." + (let* ((background (mode-icons-background-color face)) + (foreground (mode-icons-foreground-color face)) + (lst (mode-icons-desaturate-colors icon-path foreground background)) + (name (concat "mode_icons_desaturate_" + (or (and background foreground + (substring (mode-icons-interpolate background foreground 0.0) 1)) + "black") "_" + (or (and background foreground + (substring (mode-icons-interpolate background foreground 1.0) 1)) + "white") "_" + (file-name-sans-extension (file-name-nondirectory icon-path)))) + (sym (intern name))) + (or (gethash sym mode-icons-get-icon-display-xpm-bw-face) + (puthash sym (mode-icons-get-icon-display-xpm-replace icon-path lst name) mode-icons-get-icon-display-xpm-bw-face)))) + + +(defcustom mode-icons-desaturate-inactive t + "Should the inactive mode-line be desaturated. +And changed to match the icon colors? +This only works with xpm files." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-desaturate-active nil + "Should the active mode-line be desaturated. +And changed to match the icon colors? +This only works with xpm files." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-grayscale-transform t + "Should grayscale 'xpm-bw images match mode-line colors?" + :type 'boolean + :group 'mode-icons) + +(defvar mode-icons-get-icon-display (make-hash-table :test 'equal) + "Hash table of `mode-icons-get-icon-display'.") + +(defun mode-icons--get-face (&optional face active) + "If FACE is unspecified, use ACTIVE to determine the face. +ACTIVE tells if current window is active." + (or face (and active 'mode-line) 'mode-line-inactive)) + +(defcustom mode-icons-line-height-adjust 0 + "The manual adjustment of the mode-line height for images." + :type 'integer + :group 'mode-icons) + +(defun mode-icons-line-height (&optional window) + "Gets the height in pixels of WINDOW's mode-line, if accessible. +This uses `window-mode-line-height' on emacs 24.4+. Otherwise it assumes 16. + +This function also adjusts the line height by `mode-icons-line-height-adjust'." + (+ mode-icons-line-height-adjust + (or (and (fboundp 'window-mode-line-height) (window-mode-line-height window)) 16))) + +(defun mode-icons-get-icon-display (icon type &optional face active) + "Get the value for the display property of ICON having TYPE. + +ICON should be a string naming the file of the icon, without its +extension. Type should be a symbol designating the file type for +the icon. + +FACE should be the face for rendering black and white xpm icons +specified by type 'xpm-bw. + +ACTIVE is an indicator that the current window is active." + (let* ((face (mode-icons--get-face face active)) + (key (list icon type face active + mode-icons-desaturate-inactive mode-icons-desaturate-active + mode-icons-grayscale-transform custom-enabled-themes)) + tmp) + (or (gethash key mode-icons-get-icon-display) + (puthash key + (cond + ((memq type '(png xpm xpm-bw gif jpeg jpg xbm xpm)) + (let ((icon-path (mode-icons-get-icon-file + (concat icon "." (or (and (eq type 'xpm-bw) "xpm") + (symbol-name type)))))) + (cond + ((and mode-icons-grayscale-transform (eq type 'xpm-bw)) + (create-image (mode-icons-get-icon-display-xpm-bw-face icon-path face) + ;; Use imagemagick for rescaling... + (or (and (fboundp 'imagemagick-types) + (memq 'png (imagemagick-types)) 'imagemagick) + 'xpm) + t :ascent 'center + :face face + :xpm-bw t + :height (mode-icons-line-height) + :icon icon)) + ((eq type 'xpm-bw) + (create-image icon-path + (or (and (fboundp 'imagemagick-types) + (memq 'png (imagemagick-types)) 'imagemagick) + 'xpm) + :height (mode-icons-line-height) + :ascent 'center + :face face + :icon icon)) + ((and (eq type 'xpm) + (or (and active mode-icons-desaturate-active) + (and (not active) mode-icons-desaturate-inactive))) + (create-image (mode-icons-desaturate-xpm icon-path face) + (or (and (fboundp 'imagemagick-types) + (memq 'png (imagemagick-types)) 'imagemagick) + 'xpm) t + :ascent 'center + :height (mode-icons-line-height) + :face face :icon icon)) + (t + (create-image icon-path + (or (and (fboundp 'imagemagick-types) + (memq (or (and (eq type 'jpg) 'jpeg) type) (imagemagick-types)) + 'imagemagick) + (or (and (eq type 'jpg) 'jpeg) type)) + nil + :height (mode-icons-line-height) + :ascent 'center :face face :icon icon))))) + ((and (eq type 'emoji) (setq tmp (mode-icons--get-emoji " " (list "" icon type) face))) + (get-text-property 0 'display tmp)) + ;; Shouldn't get here... + ((and (eq type 'ext) (setq tmp (mode-icons--ext-available-p (list "" icon type)))) + (mode-icons-get-icon-display (concat "ext-" (downcase icon)) 'xpm-bw face active)) + ((and (image-type-available-p 'xpm) + (setq tmp (mode-icons--get-font-xpm-file (list "" icon type))) + (file-exists-p tmp)) + (setq tmp nil) + (mode-icons-get-icon-display (mode-icons--get-font-xpm-file (list "" icon type) t) 'xpm-bw face active)) + (t nil)) + mode-icons-get-icon-display)))) + +(defcustom mode-icons-minor-mode-base-text-properties + '('help-echo nil + 'mouse-face 'mode-line-highlight + 'local-map mode-line-minor-mode-keymap) + "List of text propeties to apply to every minor mode." + :type '(repeat sexp) + :group 'mode-icons) + +(defcustom mode-icons-major-mode-base-text-properties + '('help-echo "Major mode\nmouse-1: Display major mode menu\nmouse-2: Show help for major mode\nmouse-3: Toggle minor modes" + 'mouse-face 'mode-line-highlight + 'local-map mode-line-major-mode-keymap) + "List of text propeties to apply to every major mode." + :type '(repeat sexp) + :group 'mode-icons) + +(defcustom mode-icons-narrow-text-properties + '('local-map + '(keymap + (mode-line keymap + (mouse-2 . mode-line-widen))) + 'mouse-face 'mode-line-highlight 'help-echo "mouse-2: Remove narrowing from buffer") + "List of text propeties to apply to narrowing buffer indicator." + :type '(repeat sexp) + :group 'mode-icons) + +(defcustom mode-icons-read-only-text-properties + '('mouse-face 'mode-line-highlight 'local-map + '(keymap + (mode-line keymap + (mouse-1 . mode-line-toggle-read-only))) + 'help-echo 'mode-line-read-only-help-echo) + "List of text propeties to apply to read-only buffer indicator." + :type '(repeat sexp) + :group 'mode-icons) + +(defcustom mode-icons-modified-text-properties + '('mouse-face 'mode-line-highlight + 'local-map + '(keymap + (mode-line keymap + (mouse-1 . mode-icons-save-steal-or-revert-buffer) + (mouse-3 . mode-line-toggle-modified))) + 'help-echo 'mode-icons-modified-help-echo) + "List of text propeties to apply to read-only buffer indicator." + :type '(repeat sexp) + :group 'mode-icons) + +(defun mode-icons-save-steal-or-revert-buffer (event) + "Save buffer OR revert file from mode line. +Use EVENT to determine location." + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (let* ((bfn (buffer-file-name)) + (revert-p (not (or (and bfn (file-remote-p buffer-file-name)) + (verify-visited-file-modtime (current-buffer))))) + (steal-p (and (not (or (and bfn (file-remote-p buffer-file-name)) + (member (file-locked-p bfn) '(nil t))))))) + (cond + (revert-p (revert-buffer t t)) + (steal-p + (message "To steal or ignore lock, start editing the file.")) + (t (call-interactively (key-binding (where-is-internal 'save-buffer global-map t)))))) + (force-mode-line-update))) + +(defun mode-icons-modified-help-echo (window _object _point) + "Return help text specifying WINDOW's buffer modification status." + (let* ((bfn (buffer-file-name)) + (revert-p (not (or (and bfn (file-remote-p buffer-file-name)) + (verify-visited-file-modtime (current-buffer))))) + (steal-p (and (not (or (and bfn (file-remote-p buffer-file-name)) + (member (file-locked-p bfn) '(nil t)))))) + (mod-p (buffer-modified-p (window-buffer window)))) + (format "Buffer is %s\nmouse-1: %s Buffer\nmouse-3: Toggle modification state" + (cond + (steal-p + "locked for editing by another user.") + (revert-p + "modified outside of emacs!") + ((buffer-modified-p (window-buffer window)) + "modified") + (t "unmodified")) + (cond + (steal-p + "Echo about lock status of") + (revert-p + "Revert") + (mod-p + "Save") + (t ""))))) + +(defcustom mode-icons-read-only-text-properties + '('mouse-face 'mode-line-highlight 'local-map + '(keymap + (mode-line keymap + (mouse-1 . mode-line-toggle-read-only))) + 'help-echo 'mode-line-read-only-help-echo) + "List of text propeties to apply to read-only buffer indicator." + :type '(repeat sexp) + :group 'mode-icons) + +(defvar mode-icons-powerline-p nil) +(defun mode-icons-need-update-p () + "Determine if the mode-icons need an update." + (not (or (and (boundp 'rich-minority-mode) rich-minority-mode) + (member 'sml/pos-id-separator mode-line-format) + (string-match-p "powerline" (prin1-to-string mode-line-format))))) + +(defvar mode-icons-font-register-alist nil + "Alist of characters supported.") + +(defun mode-icons-supported-font-p (char font) + "Determine if the CHAR is supported in FONT. +When DONT-REGISTER is non-nil, don't register the font. +Otherwise, register the font for use in the mode-line and +everywhere else." + (if (memq font '(ext emoji xpm xbm jpg jpeg gif png nil)) nil + (unless (boundp (intern (format "mode-icons-font-spec-%s" font))) + (set (intern (format "mode-icons-font-spec-%s" font)) + (and (member (format "%s" font) (font-family-list)) + (font-spec :name (format "%s" font))))) + (when (and (or (integerp char) + (and (stringp char) (= 1 (length char)))) + (boundp (intern (format "mode-icons-font-spec-%s" font))) + (symbol-value (intern (format "mode-icons-font-spec-%s" font)))) + (let* ((char (or (and (integerp char) char) + (and (stringp char) (= 1 (length char)) + (aref (vconcat char) 0)))) + (found-char-p (assoc char mode-icons-font-register-alist)) + (char-font-p (and found-char-p (eq (cdr found-char-p) font)))) + (cond + (char-font-p t) + (found-char-p t) + (t ;; not yet registered. + (set-fontset-font t (cons char char) (symbol-value (intern (format "mode-icons-font-spec-%s" font)))) + (push (cons char font) mode-icons-font-register-alist) + t)))))) + +(defun mode-icons-supported-p (icon-spec) + "Determine if ICON-SPEC is suppored on your system." + (or + (and (or (eq (nth 2 icon-spec) nil) (eq (nth 1 icon-spec) nil)) t) + (and (eq (nth 2 icon-spec) 'emoji) + (or (and (image-type-available-p 'png) (featurep 'emojify)) + (and (image-type-available-p 'xpm) + (file-exists-p (mode-icons--get-emoji-xpm-file icon-spec))))) + (and (eq (nth 2 icon-spec) 'jpg) (image-type-available-p 'jpeg)) + (and (eq (nth 2 icon-spec) 'xpm-bw) (image-type-available-p 'xpm)) + (and (eq (nth 2 icon-spec) 'ext) (image-type-available-p 'xpm) + (mode-icons--ext-available-p icon-spec)) + (or (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) + (and (image-type-available-p 'xpm) + (mode-icons--get-font-xpm-file icon-spec) + (file-exists-p (mode-icons--get-font-xpm-file icon-spec)))) + (or (image-type-available-p (nth 2 icon-spec)) + (and (eq (nth 2 icon-spec) 'png) + (and (image-type-available-p 'xpm) + (file-exists-p (mode-icons--get-png-xpm-file icon-spec))))) )) + +(defvar emojify-emojis) + +(defvar mode-icons--gimp (executable-find "gimp") + "Gimp to convert png to xpm.") + +(defvar mode-icons--gimp-inferior-args "-i -d -b -") + +(defvar mode-icons--stop-gimp-after nil + "Seconds of idle time before mode-icons gimp is stopped. +When nil, don't stop the gimp inferior mode.") + +(defvar mode-icons--stop-gimp-timer nil) + +(defun mode-icons--start-gimp-inferior () + "GIMP inferior process." + (interactive) + (when (file-exists-p mode-icons--gimp) + (unless (get-buffer "*mode-icons-gimp*") + (cl-letf (((symbol-function 'pop-to-buffer-same-window) (lambda(&rest _ignore)))) + (save-excursion + (run-scheme (format "\"%s\" %s" mode-icons--gimp mode-icons--gimp-inferior-args)))) + (with-current-buffer (get-buffer "*scheme*") + (rename-buffer "*mode-icons-gimp*") + (set-process-query-on-exit-flag (get-buffer-process (get-buffer "*mode-icons-gimp*")) nil))))) + +(defvar mode-icons--gimp-ready-p nil) + +(defun mode-icons--gimp-ready-p () + "Determine if GIMP inferior process is ready." + (if (file-exists-p mode-icons--gimp) + (or mode-icons--gimp-ready-p + (let (buf) + (mode-icons--start-gimp-inferior) + (and (setq buf (get-buffer "*mode-icons-gimp*")) + (with-current-buffer buf + (goto-char (point-min)) + (when (re-search-forward "ts>" nil t) + (setq mode-icons--gimp-ready-p t)))))))) + +(defvar mode-icons--stop-gimp-inferior nil) +(defun mode-icons--stop-gimp-inferior () + "Stop the inferior gimp process." + (interactive) + (when (file-exists-p mode-icons--gimp) + (let ((buf (get-buffer "*mode-icons-gimp*"))) + (cond + ((and (mode-icons--gimp-ready-p) buf + (get-buffer-process buf)) + (mode-icons--process-gimp "(gimp-quit 0)") + (setq mode-icons--gimp-ready-p nil + mode-icons--stop-gimp-inferior t) + (run-with-idle-timer 1 nil #'mode-icons--stop-gimp-inferior)) + ((and buf (not (get-buffer-process buf))) + (kill-buffer (get-buffer "*mode-icons-gimp*"))) + (t (run-with-idle-timer 1 nil #'mode-icons--stop-gimp-inferior)))))) + +(defun mode-icons--process-gimp (scm) + "Process gimp SCM (scheme)." + (when mode-icons--stop-gimp-timer + (cancel-timer mode-icons--stop-gimp-timer)) + (when (file-exists-p mode-icons--gimp) + (if (mode-icons--gimp-ready-p) + (progn + (comint-send-string + (with-current-buffer (get-buffer "*mode-icons-gimp*")) + (concat scm "\n")) + (when mode-icons--stop-gimp-after + (setq mode-icons--stop-gimp-timer (run-with-timer mode-icons--stop-gimp-after nil #'mode-icons--stop-gimp-inferior)))) + (run-with-idle-timer 1 nil #'mode-icons--process-gimp scm)))) + +(defvar mode-icons--generic-type-to-xpm-gimp-script + (replace-regexp-in-string + "[ \n\t]+" " " + "(let* ((image-width 1024) + (image-height 20) + (buffer-image 1) + (text \"%s\") + (font-size 20) + (font-name \"FontAwesome\") + (xpm-image \"%s\") + (font-size-2 10) + (text-2 \"%s\") + (font-name-2 \"Haettenschweiler\") + (bg-color '(255 255 255)) + (fg-color '(0 0 0)) + (image (car (gimp-image-new 1024 16 0))) + (layer (car (gimp-layer-new image image-width image-height RGB-IMAGE \"layer 1\" 100 NORMAL))) + (layer2 (car (gimp-layer-new image image-width image-height RGB-IMAGE \"layer 2\" 100 NORMAL))) + (out-text) + (out-width) + (out-height) + (out-buffer) + (drawable)) + (gimp-image-add-layer image layer 0) + (gimp-context-set-background bg-color) + (gimp-context-set-foreground fg-color) + (gimp-layer-add-alpha layer) + (gimp-drawable-fill layer TRANSPARENT-FILL) + (gimp-image-add-layer image layer2 0) + (gimp-layer-add-alpha layer2) + (gimp-drawable-fill layer2 TRANSPARENT-FILL) + (gimp-text-fontname image layer2 3 7 text-2 0 TRUE font-size-2 PIXELS font-name-2) + (set! out-text (car (gimp-text-fontname image layer 0 0 text 0 TRUE font-size PIXELS font-name))) + (set! out-width (car (gimp-drawable-width out-text))) + (set! out-height (car (gimp-drawable-height out-text))) + (set! out-buffer (* out-height (/ buffer-image 100))) + (set! out-height (+ out-height out-buffer out-buffer)) + (set! out-width (+ out-width out-buffer out-buffer)) + (gimp-image-resize image out-width out-height 0 0) + (gimp-layer-resize layer out-width out-height 0 0) + (gimp-layer-set-offsets out-text out-buffer out-buffer) + (gimp-image-flatten image) + (set! drawable (car (gimp-image-get-active-layer image))) + (file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127) + (gimp-image-delete image))") + "Generic Type script.") + +(defvar mode-icons--font-to-xpm-gimp-script + (replace-regexp-in-string + "[ \n\t]+" " " + "(let* ((image-width 1024) + (image-height 20) + (buffer-image 1) + (text \"%s\") + (font-size 20) + (font-name \"%s\") + (xpm-image \"%s\") + (bg-color '(255 255 255)) + (fg-color '(0 0 0)) + (image (car (gimp-image-new 1024 16 0))) + (layer (car (gimp-layer-new image image-width image-height RGB-IMAGE \"layer 1\" 100 NORMAL))) + (out-text) + (out-width) + (out-height) + (out-buffer) + (drawable)) + (gimp-image-add-layer image layer 0) + (gimp-context-set-background bg-color) + (gimp-context-set-foreground fg-color) + (gimp-layer-add-alpha layer) + (gimp-drawable-fill layer TRANSPARENT-FILL) + (set! out-text (car (gimp-text-fontname image layer 0 0 text 0 TRUE font-size PIXELS font-name))) + + (set! out-width (car (gimp-drawable-width out-text))) + (set! out-height (car (gimp-drawable-height out-text))) + (set! out-buffer (* out-height (/ buffer-image 100))) + (set! out-height (+ out-height out-buffer out-buffer)) + (set! out-width (+ out-width out-buffer out-buffer)) + (gimp-image-resize image out-width out-height 0 0) + (gimp-layer-resize layer out-width out-height 0 0) + (gimp-layer-set-offsets out-text out-buffer out-buffer) + (gimp-image-flatten image) + (set! drawable (car (gimp-image-get-active-layer image))) + (file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127) + (gimp-image-delete image))") + "Gimp scheme script to convert a font character to xpm file.") + +(defvar mode-icons--convert-ext-to-xpm (make-hash-table :test 'equal)) +(defun mode-icons--convert-ext-to-xpm (ext) + "Convert EXT to a xpm file." + (let ((xpm (mode-icons-get-icon-file (concat "ext-" (downcase ext) ".xpm")))) + (when (and mode-icons--gimp (file-exists-p mode-icons--gimp) + xpm (not (gethash xpm mode-icons--convert-ext-to-xpm)) + (not (file-exists-p xpm))) + (puthash xpm t mode-icons--convert-ext-to-xpm) + (mode-icons--process-gimp + (format mode-icons--generic-type-to-xpm-gimp-script (make-string 1 #xf016) xpm + (downcase ext)))))) + +(defun mode-icons--ext-available-p (icon-spec) + "Determine if ICON-SPEC's ext is availble for display. +If not, try `mode-icons--convert-ext-to-xpm'." + (when (eq (nth 2 icon-spec) 'ext) + (let ((xpm (mode-icons-get-icon-file (concat "ext-" (downcase (nth 1 icon-spec)) ".xpm")))) + (if (file-readable-p xpm) + xpm + (mode-icons--convert-ext-to-xpm (nth 1 icon-spec)) + nil)))) + +(defcustom mode-icons-generate-font-grayscale nil + "Generate grayscale images for font icons. +This is used instead of transparancy to capure the font's +anti-aliasing. `mode-icons' will transform the colors to match +the background instead." + :type 'boolean + :group 'mode-icons) + +(defvar mode-icons--convert-text-to-xpm (make-hash-table :test 'equal)) +(defun mode-icons--convert-text-to-xpm (text font xpm &optional face height) + "Convert TEXT in FONT to XPM file using gimp. + +When FACE is non-nil, use the face background and foreground +properties to render the font (its no longer transparent). + +When HEIGHT is non-nil, use the font HEIGHT (in pixels) instead +of 20px." + (when (and mode-icons--gimp (file-exists-p mode-icons--gimp) + xpm (not (gethash xpm mode-icons--convert-text-to-xpm)) + (not (file-exists-p xpm))) + (puthash xpm t mode-icons--convert-text-to-xpm) + (let ((script (format mode-icons--font-to-xpm-gimp-script text font xpm)) + (background (mode-icons-background-color face)) + (foreground (mode-icons-foreground-color face))) + (when face + (setq background (mapcar (lambda(x) + (round (* 255 x))) background) + foreground (mapcar (lambda(x) + (round (* 255 x))) foreground)) + (setq script (replace-regexp-in-string + (regexp-quote "(bg-color '(255 255 255))") + (format "(bg-color '%s)" background) + script) + script (replace-regexp-in-string + (regexp-quote "(fg-color '(0 0 0))") + (format "(fg-color '%s)" foreground) + script) + script (replace-regexp-in-string + "TRANSPARENT-FILL" "BACKGROUND-FILL" script) + script (replace-regexp-in-string + (regexp-quote "(gimp-layer-add-alpha layer)") "" script))) + (when height + (setq script (replace-regexp-in-string + (regexp-quote "(image-height 20)") + (format "(image-height %s)" background) + script) + script (replace-regexp-in-string + (regexp-quote "(font-size 20)") + (format "(font-size %s)" background) + script) + script (replace-regexp-in-string + "TRANSPARENT-FILL" "BACKGROUND-FILL" script) + script (replace-regexp-in-string + (regexp-quote "(gimp-layer-add-alpha layer)") "" script))) + (when mode-icons-generate-font-grayscale + (setq script (replace-regexp-in-string + "TRANSPARENT-FILL" "BACKGROUND-FILL" script) + script (replace-regexp-in-string + (regexp-quote "(gimp-layer-add-alpha layer)") "" script))) + (mode-icons--process-gimp script)))) + +(defun mode-icons--get-font-xpm-file (icon-spec &optional icon-name) + "Get the font icon equivalent xpm file name from ICON-SPEC. +When ICON-NAME is non-nil, return the small icon name without the +extension or directory." + (let* ((xpm-int (or (and (stringp (nth 1 icon-spec)) + (= 1 (length (nth 1 icon-spec))) + (aref (nth 1 icon-spec) 0)) + (and (integerp (nth 1 icon-spec)) + (nth 1 icon-spec)))) + (xpm-base (and (integerp xpm-int) + (format "%s-%x" (nth 2 icon-spec) + xpm-int)))) + (and xpm-base + (if icon-name + xpm-base + (mode-icons-get-icon-file (concat xpm-base ".xpm")))))) + +(defun mode-icons--create-font-xpm-file (icon-spec) + "Create a font-based xpm file based on ICON-SPEC." + (mode-icons--convert-text-to-xpm + (or (and (stringp (nth 1 icon-spec)) + (nth 1 icon-spec)) + (and (integerp (nth 1 icon-spec)) + (make-string 1 (nth 1 icon-spec)))) + (symbol-name (nth 2 icon-spec)) + (mode-icons--get-font-xpm-file icon-spec))) + +(defun mode-icons--convert-all-font-icons-to-xpm () + "Convert all font icons to xpm files." + (interactive) + (setq mode-icons--convert-text-to-xpm (make-hash-table :test 'equal)) + (dolist (icon-spec mode-icons) + (when (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) + (mode-icons--create-font-xpm-file icon-spec)))) + +(defvar mode-icons--png-to-xpm-gimp-script + (replace-regexp-in-string + "[ \n\t]+" " " + "(let* ((png-image \"%s\") + (xpm-image \"%s\") + (image (car (file-png-load RUN-NONINTERACTIVE png-image png-image))) + (drawable (car (gimp-image-get-active-layer image))) + (width (car (gimp-image-width image))) + (height (car (gimp-image-height image))) + (new-height 16.0) + (new-width (inexact->exact (round (* width (/ new-height height)))))) + (gimp-image-resize image 16 new-width 0 0) + (set! drawable (car (gimp-image-get-active-layer image))) + (file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127) + (gimp-image-delete image))") + "Gimp scheme script to convert png to xpm.") + +(defvar mode-icons--convert-png-to-xpm (make-hash-table :test 'equal) + "Hash table to make sure you only convert once.") + +(defun mode-icons--convert-png-to-xpm (png xpm) + "Covert PNG to a ?x16 XPM using `mode-icons--gimp'." + (when (and mode-icons--gimp (file-exists-p mode-icons--gimp) + xpm (not (gethash (list png xpm) mode-icons--convert-png-to-xpm)) + (not (file-exists-p xpm))) + (puthash (list png xpm) t mode-icons--convert-png-to-xpm) + (mode-icons--process-gimp (format mode-icons--png-to-xpm-gimp-script png xpm)))) + +(defun mode-icons--get-png-xpm-file (icon-spec &optional icon-name) + "Get the png->xpm file name from ICON-SPEC. + +When ICON-NAME is non-nil, return the mode-icons icon name." + (if icon-name + (nth 1 icon-spec) + (mode-icons-get-icon-file (concat (nth 1 icon-spec) ".xpm")))) + +(defun mode-icons--convert-all-png-icons-to-xpm () + "Convert all png icons to xpm files." + (interactive) + (setq mode-icons--convert-png-to-xpm (make-hash-table :test 'equal)) + (dolist (icon-spec mode-icons) + (when (eq 'png (nth 2 icon-spec)) + (mode-icons--convert-png-to-xpm + (mode-icons-get-icon-file (concat (nth 1 icon-spec) ".png")) + (mode-icons-get-icon-file (concat (nth 1 icon-spec) ".xpm")))))) + +(defun mode-icons--get-emoji-xpm-file (icon-spec &optional icon-name) + "Get the emoji xpm file name from ICON-SPEC. +This only supports emoji enclosed in a \":\" like :herb:. + +When ICON-NAME is non-nil, return the mode-icons icon name. +For :herb: it would be e-herb." + (let* ((xpm-base (nth 1 icon-spec)) + file) + (when (char-equal (aref xpm-base 0) ?:) + (setq file (substring xpm-base 1)) + (when (char-equal (aref (substring xpm-base -1) 0) ?:) + (setq file (substring file 0 -1)) + (if icon-name + (concat "e-" file) + (mode-icons-get-icon-file (concat "e-" file ".xpm"))))))) + +(defun mode-icons--get-png (mode icon-spec &optional face active) + "Get MODE for png ICON-SPEC using FACE. +If possible, convert the png file to an xpm file. +ACTIVE is a flag telling if the current window is active." + (let* ((xpm (mode-icons--get-png-xpm-file icon-spec)) + (xpm-name (mode-icons--get-png-xpm-file icon-spec t)) + (xpm-p (file-readable-p xpm)) + (png (mode-icons-get-icon-file (concat (nth 1 icon-spec) ".png"))) + (png-p (file-readable-p png)) + (face (mode-icons--get-face face active))) + (if xpm-p + (propertize (format "%s" mode) 'display + (mode-icons-get-icon-display + xpm-name 'xpm + face active) + 'face face + 'mode-icons-p (list (nth 0 icon-spec) xpm-name 'xpm)) + (if (not png-p) + (propertize (format "%s" mode) + 'face face + 'mode-icons-p icon-spec) + (mode-icons--convert-png-to-xpm png xpm) + (propertize (format "%s" mode) + 'display + (create-image png + ;; use imagemagick if available and supports PNG images + ;; (allows resizing images) + (or (and (and (fboundp 'imagemagick-types) + (memq 'png (imagemagick-types))) + 'imagemagick) 'png) + nil + :height (mode-icons-line-height) + :ascent 'center + :heuristic-mask t + :face face) + 'face face + 'mode-icons-p icon-spec))))) + +(defcustom mode-icons-prefer-xpm-over-emoji nil + "Prefer generated xpms over fonts. +If mode-icons has a generated font character, prefer that over +the actual font." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-generate-emoji-xpms nil + "Generate font compatibility xpms for fonts." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons--get-emoji (mode icon-spec &optional face active) + "Get MODE emoji for ICON-SPEC using FACE. +ACTIVE is a flag for if the current window is active." + (let* ((xpm (mode-icons--get-emoji-xpm-file icon-spec)) + (xpm-name (mode-icons--get-emoji-xpm-file icon-spec t)) + (xpm-p (file-readable-p xpm)) + (face (mode-icons--get-face face active))) + (if (or (and mode-icons-prefer-xpm-over-emoji xpm-p) + (and xpm-p (not (featurep 'emojify))) + (and xpm-p (not (image-type-available-p 'png)))) + (propertize (format "%s" mode) 'display + (mode-icons-get-icon-display + xpm-name 'xpm face active) + 'mode-icons-p (list (nth 0 icon-spec) xpm-name 'xpm)) + (unless emojify-emojis + (emojify-set-emoji-data)) + (let* ((emoji (ht-get emojify-emojis (nth 1 icon-spec))) + (image-file (expand-file-name (ht-get emoji "image") (if (fboundp 'emojify-image-dir) + (emojify-image-dir) + emojify-image-dir))) + (image-type (intern (upcase (file-name-extension image-file))))) + (if (not (file-exists-p image-file)) + (propertize (format "%s" mode) + 'face face + 'mode-icons-p icon-spec) + (when mode-icons-generate-emoji-xpms + (mode-icons--convert-png-to-xpm image-file xpm)) + (propertize (format "%s" mode) + 'display + (create-image image-file + ;; use imagemagick if available and supports PNG images + ;; (allows resizing images) + (or (and (and (fboundp 'imagemagick-types) + (memq image-type (imagemagick-types))) + 'imagemagick) 'png) + nil + :ascent 'center + :heuristic-mask t + :face face + ;; :background (emojify--get-image-background beg end) + ;; no-op if imagemagick is not available + :height (mode-icons-line-height)) + 'face face + 'mode-icons-p icon-spec)))))) + +(defcustom mode-icons-prefer-xpm-over-font nil + "Prefer generated xpms over fonts. +If mode-icons has a generated font character, prefer that over +the actual font." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-generate-font-xpms nil + "Generate font compatibility xpms for fonts." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons--get-font (mode icon-spec &optional face active) + "Get font for MODE based on ICON-SPEC, and FACE. +ACTIVE if a flag for if the current window is active." + ;; Use `compose-region' because it allows clickable text. + (let* ((xpm (mode-icons--get-font-xpm-file icon-spec)) + (xpm-name (mode-icons--get-font-xpm-file icon-spec t)) + (xpm-p (file-readable-p xpm)) + (face (mode-icons--get-face face active))) + (when (and (not xpm-p) mode-icons-generate-font-xpms) + (mode-icons--create-font-xpm-file icon-spec)) + (if (and xpm-p (or mode-icons-prefer-xpm-over-font + (not (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec))))) + (propertize (format "%s" mode) 'display + (mode-icons-get-icon-display + xpm-name 'xpm face active) + 'mode-icons-p (list (nth 0 icon-spec) xpm-name 'xpm-bw) + 'face face) + (mode-icons-save-buffer-state + (with-temp-buffer + (if (stringp mode) + (insert mode) + (insert (or (and (integerp (nth 1 icon-spec)) + (make-string 1 (nth 1 icon-spec))) + (nth 1 icon-spec)))) + (compose-region (point-min) (point-max) (or (and (integerp (nth 1 icon-spec)) + (make-string 1 (nth 1 icon-spec))) + (nth 1 icon-spec))) + (put-text-property (point-min) (point-max) + 'face face) + (if (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) + (put-text-property (point-min) (point-max) + 'mode-icons-p icon-spec) + (put-text-property (point-min) (point-max) + 'mode-icons-p (list (nth 0 icon-spec) xpm-name 'xpm-bw))) + (buffer-string)))))) + +(defun mode-icons-propertize-mode (mode icon-spec &optional face active) + "Propertize MODE with ICON-SPEC. + +MODE should be a string, the name of the mode to propertize. +ICON-SPEC should be a specification from `mode-icons'. +FACE is the face to match when a xpm-bw image is used. +ACTIVE is a flag to tell if the current window is active." + (let (tmp new-icon-spec) + (mode-icons-save-buffer-state ;; Otherwise may cause issues with trasient mark mode + (cond + ((and (stringp mode) (get-text-property 0 'mode-icons-p mode)) + mode) + ((not (nth 1 icon-spec)) + "") + ((and (stringp (nth 1 icon-spec)) (not (nth 2 icon-spec))) + (propertize (nth 1 icon-spec) 'display (nth 1 icon-spec) + 'mode-icons-p icon-spec)) + ((mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) + ;; (propertize mode 'display (nth 1 icon-spec) 'mode-icons-p t) + ;;(mode-icons--get-font " AI" '("\\` ?AI\\'" 61500 FontAwesome) face active) + (mode-icons--get-font mode icon-spec face active)) + ((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'emoji)) + (mode-icons--get-emoji mode icon-spec face active)) + ((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'png)) + (mode-icons--get-png mode icon-spec face active)) + ((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'ext)) + (propertize (format "%s" mode) 'display + (mode-icons-get-icon-display + (concat "ext-" (nth 1 icon-spec)) 'xpm-bw face active) + 'mode-icons-p (list (nth 0 icon-spec) + (concat "ext-" (nth 1 icon-spec)) + 'xpm-bw))) + (t (setq tmp (mode-icons-get-icon-display (nth 1 icon-spec) (nth 2 icon-spec) face active)) + ;; (when (string= (nth 0 icon-spec) "\\` ?AI\\'") + ;; (message "plist: %s" tmp)) + (cond + ((and (plist-get (cdr tmp) :xpm-bw) (plist-get (cdr tmp) :icon)) + (setq new-icon-spec (list (nth 0 icon-spec) (plist-get (cdr tmp) :icon) 'xpm-bw))) + ((and (eq (plist-get (cdr tmp) :type) 'xpm) (plist-get (cdr tmp) :icon)) + (setq new-icon-spec (list (nth 0 icon-spec) (plist-get (cdr tmp) :icon) 'xpm))) + (t (setq new-icon-spec icon-spec))) + (propertize (format "%s" mode) 'display tmp + 'mode-icons-p new-icon-spec)))))) + +(defvar mode-icons-get-icon-spec (make-hash-table :test 'equal) + "Hash table of icon-specifications.") +(defun mode-icons-get-icon-spec (mode &optional is-major-mode-p) + "Get icon spec for MODE based on regular expression." + (or (gethash mode mode-icons-get-icon-spec) + (let* (case-fold-search + (ignore-cache nil) + (icon-spec (catch 'found-mode + (dolist (item mode-icons) + (when (and (mode-icons-supported-p item) + (or + (and + (stringp (car item)) + (stringp mode) + (string-match-p (car item) mode)) + (and + (symbolp (car item)) + (symbolp mode) + (eq mode (car item))) + (and + is-major-mode-p + (symbolp (car item)) + (functionp (car item)) + (and (ignore-errors (funcall (car item))) + (setq ignore-cache t))))) + (throw 'found-mode item))) + nil))) + (when (and icon-spec (eq (nth 2 icon-spec) 'emoji) + (file-exists-p (mode-icons--get-emoji-xpm-file icon-spec))) + (setq icon-spec (list (nth 0 icon-spec) (mode-icons--get-emoji-xpm-file icon-spec t) 'xpm))) + (unless ignore-cache + (puthash mode icon-spec mode-icons-get-icon-spec)) + icon-spec))) + +(defcustom mode-icons-show-mode-name nil + "Show Icon and `mode-name'." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-change-mode-name t + "Change the `mode-name' variable. + +This allows functions like `ibuffer' or `helm-mode' to show the +icon as well." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-use-default-icon nil + "Use the 'default icon when icon-name cannot be found." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons-get-mode-icon (mode &optional face active) + "Get the icon for MODE, if there is one. +FACE represents the face used when the icon is a xpm-bw image. +ACTIVE represents if the window is active." + (let* ((mode-name (format-mode-line mode)) + (icon-spec (mode-icons-get-icon-spec mode-name t)) + (face (mode-icons--get-face face active)) + ret) + (when (and (not icon-spec) mode-icons-use-default-icon) + (setq icon-spec (mode-icons-get-icon-spec 'default))) + (if icon-spec + (setq ret + (if mode-icons-show-mode-name + (concat (mode-icons-propertize-mode mode-name icon-spec face active) " " mode-name) + (mode-icons-propertize-mode mode-name icon-spec face active))) + (setq ret mode-name)) + ;; Don't hide major mode names... + (when (string= ret "") + (setq ret mode-name)) + ret)) + +(defvar mode-icons-cached-mode-name nil + "Cached mode name to restore when disabling mode-icons.") + +(defvar mode-icons--mode-name nil + "Mode name displayed by mode-icons.") + +(defun mode-icons-set-mode-icon (mode) + "Set the icon for MODE." + (unless mode-icons-cached-mode-name + (set (make-local-variable 'mode-icons-cached-mode-name) + mode-name) + (set (make-local-variable 'mode-icons--mode-name) + (mode-icons-get-mode-icon mode nil t)) + (when mode-icons-change-mode-name + (setq mode-name mode-icons--mode-name)))) + +(defun mode-icons-major-mode-icons-undo () + "Undo the `mode-name' icons." + (dolist (b (buffer-list)) + (with-current-buffer b + (when mode-icons-cached-mode-name + (setq mode-name mode-icons-cached-mode-name + mode-icons-cached-mode-name nil))))) + +(defun mode-icons-major-mode-icons () + "Apply mode name icons on all buffers." + (dolist (b (buffer-list)) + (with-current-buffer b + (mode-icons-set-current-mode-icon)))) + +(defun mode-icons-set-current-mode-icon () + "Set the icon for the current major mode." + (mode-icons-set-mode-icon mode-name)) + +(defvar mode-icons-set-minor-mode-icon-alist nil) + +(defun mode-icons-set-minor-mode-icon-undo (&optional dont-update) + "Undo minor modes. +When DONT-UPDATE is non-nil, don't call `force-mode-line-update'." + (let (minor) + (dolist (mode mode-icons-set-minor-mode-icon-alist) + (setq minor (assq (car mode) minor-mode-alist)) + (when minor + (setcdr minor (cdr mode))))) + (setq mode-icons-set-minor-mode-icon-alist nil) + (unless dont-update + (force-mode-line-update))) + +(defcustom mode-icons-separate-images-with-spaces t + "Separate minor-mode icons with spaces." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons-set-minor-mode-icon (&optional dont-update) + "Set the icon for the minor modes. +When DONT-UPDATE is non-nil, don't call `force-mode-line-update'" + (let (icon-spec mode-name minor cur-mode) + (dolist (mode minor-mode-alist) + (setq cur-mode + (or (assq (car mode) mode-icons-set-minor-mode-icon-alist) + mode)) + (setq mode-name (format-mode-line cur-mode) + icon-spec (mode-icons-get-icon-spec mode-name)) + (when icon-spec + (setq minor (assq (car cur-mode) minor-mode-alist)) + (when minor + (or (assq (car cur-mode) mode-icons-set-minor-mode-icon-alist) + (push (copy-sequence minor) mode-icons-set-minor-mode-icon-alist)) + (setq mode-name (replace-regexp-in-string "^ " "" mode-name) + mode-name (mode-icons-propertize-mode mode-name icon-spec)) + (if (string= "" mode-name) + (setcdr minor (list "")) + (setcdr minor (list (concat (or (and mode-icons-separate-images-with-spaces " ") "") + mode-name)))))))) + (unless dont-update + (force-mode-line-update))) + +(defun mode-icons--generate-major-mode-item (&optional face) + "Give rich strings needed for `major-mode' viewing. +FACE is the face that the major mode item should be rendered in." + (let* ((active (mode-icons--selected-window-active)) + (face (mode-icons--get-face face active))) + (eval `(propertize ,(mode-icons--recolor-string (or mode-icons--mode-name mode-name) active face) + 'face ',face + ,@mode-icons-major-mode-base-text-properties)))) + +;;; selected take from powerline +(defvar mode-icons--selected-window (frame-selected-window) + "Selected window.") + +(defun mode-icons--set-selected-window () + "Set the variable `mode-icons--selected-window' appropriately." + (when (not (minibuffer-window-active-p (frame-selected-window))) + (setq mode-icons--selected-window (frame-selected-window)))) + +(defun mode-icons--unset-selected-window () + "Unsets the variable `mode-icons--selected-window' and update the modeline." + (setq mode-icons--selected-window nil) + (force-mode-line-update)) + +(add-hook 'window-configuration-change-hook 'mode-icons--set-selected-window) + +;; focus-in-hook was introduced in emacs v24.4. +;; Gets evaluated in the last frame's environment. +;; (add-hook 'focus-in-hook 'mode-icons--set-selected-window) + +;; focus-out-hook was introduced in emacs v24.4. +;; (add-hook 'focus-out-hook 'mode-icons--unset-selected-window) + +;; Executes after the window manager requests that the user's events +;; be directed to a different frame. +(defadvice handle-switch-frame + (after mode-icons--set-selected-window-after-switch-frame activate) + "Make `mode-icons' aware of selected window." + (mode-icons--set-selected-window)) + +(defadvice select-window (after mode-icons--select-window activate) + "Make `mode-icons' aware of selected window." + (mode-icons--set-selected-window)) + +(defun mode-icons--selected-window-active () + "Return whether the current window is active." + (eq mode-icons--selected-window (selected-window))) + +(defun mode-icons--property-substrings (str prop) + "Return a list of substrings of STR when PROP change." + ;; Taken from powerline by Donald Ephraim Curtis, Jason Milkins and + ;; Nicolas Rougier + (let ((beg 0) (end 0) + (len (length str)) + (out)) + (while (< end (length str)) + (setq end (or (next-single-property-change beg prop str) len)) + (setq out (append out (list (substring str beg (setq beg end)))))) + out)) + +(defun mode-icons--recolor-string (string &optional active face) + "Recolor `mode-icons' in STRING. +ACTIVE tells if the current window is active. +FACE is the face to recolor the icon to." + (let* ((face (mode-icons--get-face face active)) + icon-spec) + (mapconcat + (lambda(str) + (cond + ((get-text-property 0 'display str) + (mode-icons--recolor-minor-mode-image str active face)) + ((and (setq icon-spec (get-text-property 0 'mode-icons-p str)) + (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec))) + (mode-icons--get-font str icon-spec face active)) + (t + str))) + (mode-icons--property-substrings string 'mode-icons-p) + ""))) + +(defun mode-icons--recolor-minor-mode-image (mode active &optional face) + "Recolor MODE image based on if the window is ACTIVE. +Use FACE when specified." + (let ((icon-spec (get-text-property 0 'mode-icons-p mode)) + (face (mode-icons--get-face face active))) + (cond + ((and icon-spec (memq (nth 2 icon-spec) '(xpm xpm-bw))) + (propertize mode 'display (mode-icons-get-icon-display + (nth 1 icon-spec) (nth 2 icon-spec) face active) + 'face face + 'mode-icons-p icon-spec)) + ((and icon-spec (memq (nth 2 icon-spec) '(emoji)) + (file-exists-p (mode-icons--get-emoji-xpm-file icon-spec))) + (propertize mode 'display (mode-icons-get-icon-display + (mode-icons--get-emoji-xpm-file icon-spec t) + 'xpm face active) 'face face + 'mode-icons-p icon-spec)) + (t (propertize mode 'face face))))) + +(defun mode-icons--generate-minor-mode-list (&optional face) + "Extracts all rich strings necessary for the minor mode list. +When FACE is non-nil, use FACE to render the `minor-mode-alist'." + (let* ((active (mode-icons--selected-window-active)) + (face (mode-icons--get-face face active))) + (delete " " (delete "" (mapcar (lambda(mode) + (concat " " (eval `(propertize ,(mode-icons--recolor-minor-mode-image mode active face) + ,@mode-icons-minor-mode-base-text-properties)))) + (split-string (format-mode-line minor-mode-alist))))))) + +(defun mode-icons--generate-narrow (&optional face) + "Extracts all rich strings necessary for narrow indicator. +When FACE is non-nil, use FACE to render the narrow indicator." + (let* ((active (mode-icons--selected-window-active)) + (face (mode-icons--get-face active face)) + icon-spec) + (delete " " (delete "" (mapcar (lambda(mode) + (concat " " (eval `(propertize + ,(if (setq icon-spec (mode-icons-get-icon-spec (concat " " mode))) + (mode-icons--recolor-minor-mode-image + (mode-icons-propertize-mode (concat " " mode) icon-spec face active) + active face) + mode) + ,@mode-icons-narrow-text-properties)))) + (split-string (format-mode-line "%n"))))))) + + +(defcustom mode-icons-read-only-space t + "Add Space after read-only icon." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons--read-only-status (&optional face) + "Get Read Only Status icon. +FACE is the face to render the icon in." + (let ((active (mode-icons--selected-window-active))) + (eval `(propertize + ,(let ((ro (format-mode-line "%1*")) + icon-spec) + (setq ro (or (cond + ((string= "%" ro) + (if (setq icon-spec (mode-icons-get-icon-spec 'read-only)) + (mode-icons-propertize-mode 'read-only icon-spec face active) + ro)) + (t + (if (setq icon-spec (mode-icons-get-icon-spec 'writable)) + (mode-icons-propertize-mode 'writable icon-spec face active) + ro))) + "") + ro (mode-icons--recolor-minor-mode-image ro active face)) + (when (and mode-icons-read-only-space + (not (string= ro ""))) + (setq ro (concat ro " "))) + ro) + ,@mode-icons-read-only-text-properties)))) + +(defcustom mode-icons-modified-status-space t + "Add Space to modified status." + :type 'boolean + :group 'mode-icons) + +(defun mode-icons--modified-status (&optional face) + "Get modified status icon. +FACE is the face to render the icon in." + (let ((active (mode-icons--selected-window-active))) + (eval `(propertize + ,(or (ignore-errors + (let* ((bfn (buffer-file-name)) + (nice-file-p (and (file-remote-p bfn))) + (mod (or (and (not (or nice-file-p (verify-visited-file-modtime (current-buffer)))) + "!") + (and (not (or nice-file-p (member (file-locked-p bfn) '(nil t)))) + "s") + (format-mode-line "%1+"))) + icon-spec) + (setq mod (or (cond + ((not (stringp mod)) "") + ((char-equal ?s (aref mod 0)) + (if (setq icon-spec (mode-icons-get-icon-spec 'steal)) + (mode-icons-propertize-mode 'steal icon-spec face active) + mod)) + ((char-equal ?! (aref mod 0)) + (if (setq icon-spec (mode-icons-get-icon-spec 'modified-outside)) + (mode-icons-propertize-mode 'modified-outside icon-spec face active) + mod)) + ((char-equal ?* (aref mod 0)) + (if (setq icon-spec (mode-icons-get-icon-spec 'save)) + (mode-icons-propertize-mode 'save icon-spec face active) + mod)) + (t + (if (setq icon-spec (mode-icons-get-icon-spec 'saved)) + (mode-icons-propertize-mode 'saved icon-spec face active) + mod))) + "")) + (setq mod (mode-icons--recolor-minor-mode-image mod active face)) + (when (and mode-icons-modified-status-space + (stringp mod) + (not (string= mod ""))) + (setq mod (concat mod " "))) + mod)) "") + ,@mode-icons-modified-text-properties)))) + +;; Based on rich-minority by Artur Malabarba +(defvar mode-icons--backup-construct nil) +(defvar mode-icons--mode-line-construct + '(:eval (mode-icons--generate-minor-mode-list)) + "Construct used to replace `minor-mode-alist'.") + +(defvar mode-icons--major-backup-construct nil) +(defvar mode-icons--major-construct + '(:eval (mode-icons--generate-major-mode-item)) + "Construct used to replace `mode-name'.") + +(defvar mode-icons--narrow-backup-construct nil) +(defvar mode-icons--narrow-construct + '(:eval (mode-icons--generate-narrow)) + "Construct used to replace %n in `mode-line-modes'.") + + +(defvar mode-icons--read-only-backup-construct nil) +(defvar mode-icons--read-only-construct + '(:eval (mode-icons--read-only-status)) + "Construct used to replace %1* in `mode-line-modified'.") + + +(defvar mode-icons--modified-backup-construct nil) +(defvar mode-icons--modified-construct + '(:eval (mode-icons--modified-status)) + "Construct used to replace %1+ in `mode-line-modified'.") + +(defvar mode-icons--backup-eol-construct nil) +(defvar mode-icons--eol-construct + '(:eval (mode-icons--mode-line-eol-desc)) + "End of Line Construct.") + +(defcustom mode-icons-eol-space t + "Add a space to the end of line specification." + :type 'boolean + :group 'mode-icons) + +(defcustom mode-icons-eol-text nil + "Describe end of line type. +\(Unix) -> LF +\(DOS) -> CRLF +\(Mac) -> CR" + :type 'boolean + :group 'mode-icons) + +(defun mode-icons--mode-line-eol-desc (&optional string face) + "Modify `mode-line-eol-desc' to have icons. + +STRING is the string to modify, or if absent, the value from +`mode-line-eol-desc'. + +FACE is the face that will be used to render the segment." + (let* ((str (or string (mode-line-eol-desc))) + (props (text-properties-at 0 str)) + (lt2 "") + (active (mode-icons--selected-window-active)) + icon-spec) + (setq str (or (cond + ((string= "(Unix)" str) + (setq lt2 " LF") + (if (setq icon-spec (mode-icons-get-icon-spec 'unix)) + (mode-icons-propertize-mode 'unix icon-spec face active) + str)) + ((or (string= str "(DOS)") + (string= str "\\")) + (setq lt2 " CRLF") + (if (setq icon-spec (mode-icons-get-icon-spec 'win)) + (mode-icons-propertize-mode 'win icon-spec face active) + str)) + ((string= str "(Mac)") + (setq lt2 " CR") + (if (setq icon-spec (mode-icons-get-icon-spec 'apple)) + (mode-icons-propertize-mode 'apple icon-spec face active) + str)) + ((string= str ":") + (setq lt2 " Undecided") + (if (setq icon-spec (mode-icons-get-icon-spec 'undecided)) + (mode-icons-propertize-mode 'undecided icon-spec face active) + str)) + (t str)) + "")) + (setq str (mode-icons--recolor-minor-mode-image str active face)) + (when mode-icons-eol-text + (setq str (concat str lt2))) + (when (and mode-icons-eol-space + (not (string= "" str))) + (setq str (concat str " "))) + (add-text-properties 0 (length str) props str) + str)) + + +(defun mode-icons-fix (&optional enable) + "Fix mode-icons. +When ENABLE is non-nil, enable the changes to the mode line." + (if enable + (let ((place (or (member 'minor-mode-alist mode-line-modes) + (cl-member-if + (lambda (x) (and (listp x) + (equal (car x) :propertize) + (equal (cadr x) '("" minor-mode-alist)))) + mode-line-modes))) + (place-major (cl-member-if + (lambda(x) + (and (listp x) + (equal (car x) :propertize) + (equal (cadr x) '("" mode-name)))) + mode-line-modes)) + (place-narrow (cl-member-if + (lambda(x) + (and (stringp x) (string= "%n" x))) + mode-line-modes)) + (place-ro (cl-member-if + (lambda(x) + (and (stringp x) (string-match-p "%[0-9]*[*]" x))) + mode-line-modified)) + (place-mod (cl-member-if + (lambda(x) + (and (stringp x) (string-match-p "%[0-9]*[+]" x))) + mode-line-modified)) + (place-eol (cl-member-if + (lambda(x) + (and (listp x) + (equal (car x) :eval) + (eq (cl-caadr x) 'mode-line-eol-desc))) + mode-line-mule-info))) + (when place + (setq mode-icons--backup-construct (car place)) + (setcar place mode-icons--mode-line-construct)) + (when place-major + (setq mode-icons--major-backup-construct (car place-major)) + (setcar place-major mode-icons--major-construct)) + (when place-narrow + (setq mode-icons--narrow-backup-construct (car place-narrow)) + (setcar place-narrow mode-icons--narrow-construct)) + (when place-ro + (setq mode-icons--read-only-backup-construct (car place-ro)) + (setcar place-ro mode-icons--read-only-construct)) + (when place-mod + (setq mode-icons--modified-backup-construct (car place-mod)) + (setcar place-mod mode-icons--modified-construct)) + (when place-eol + (setq mode-icons--backup-eol-construct (car place-eol)) + (setcar place-eol mode-icons--eol-construct))) + (let ((place (member mode-icons--mode-line-construct mode-line-modes)) + (place-major (member mode-icons--major-construct mode-line-modes)) + (place-narrow (member mode-icons--narrow-construct mode-line-modes)) + (place-ro (member mode-icons--read-only-construct mode-line-modified)) + (place-mod (member mode-icons--modified-construct mode-line-modified)) + (place-eol (member mode-icons--eol-construct mode-line-mule-info))) + (when place + (setcar place mode-icons--backup-construct)) + (when place-major + (setcar place-major mode-icons--major-backup-construct)) + (when place-narrow + (setcar place-narrow mode-icons--narrow-backup-construct)) + (when place-ro + (setcar place-ro mode-icons--read-only-backup-construct)) + (when place-mod + (setcar place-mod mode-icons--modified-backup-construct)) + (when place-eol + (setcar place-eol mode-icons--backup-eol-construct))))) + +;;;###autoload +(define-minor-mode mode-icons-mode + "Replace the name of the current major mode with an icon." + :global t + (if mode-icons-mode + (progn + (add-hook 'after-change-major-mode-hook #'mode-icons-reset) + (mode-icons-fix t) + (mode-icons-set-minor-mode-icon) + (mode-icons-major-mode-icons)) + (remove-hook 'after-change-major-mode-hook #'mode-icons-reset) + (mode-icons-set-minor-mode-icon-undo) + (mode-icons-major-mode-icons-undo) + (mode-icons-fix))) + +(defun mode-icons-reset-hash () + "Reset `mode-icons-get-icon-spec' and `mode-icons-get-icon-display'." + (interactive) + (setq mode-icons-get-icon-spec (make-hash-table :test 'equal) + mode-icons-get-icon-display (make-hash-table :test 'equal))) + +(defun mode-icons-reset () + "Reset mode-icons icons." + (interactive) + (when (and mode-icons-mode (not (minibufferp))) + ;; Reset the major mode now. + (mode-icons-set-current-mode-icon) + ;; Reset the minor mode later, in case the mode turns on some + ;; minor-modes. + (run-with-idle-timer + 0.1 nil `(lambda() + ;; Reset the minor mode icons + (when (buffer-live-p ,(current-buffer)) + (with-current-buffer ,(current-buffer) + (mode-icons-set-minor-mode-icon))))))) + +(add-hook 'emacs-startup-hook #'mode-icons-reset) + +(defadvice isearch-mode (after mode-icons--reset-isearch-icon activate) + "Make `mode-icons' aware of icon." + (mode-icons-set-minor-mode-icon)) + +(eval-after-load 'powerline + '(progn + (declare-function mode-icons--real-powerline-minor-modes "powerline") + (fset 'mode-icons--real-powerline-minor-modes #'powerline-minor-modes) + (defun mode-icons--powerline-minor-modes (&optional face pad) + "Powerline minor modes is replaced by this function. +FACE is the face to use. +PAD is the padding around the minor modes. + +The original is called if `mode-icons-mode' is disabled. It is +saved in `mode-icons--real-powerline-minor-modes'." + (if mode-icons-mode + (mode-icons--generate-minor-mode-list face) + (mode-icons--real-powerline-minor-modes face pad))) + (fset 'mode-icons--real-powerline-major-mode #'powerline-minor-modes) + (defun mode-icons--powerline-major-mode (&optional face pad) + "Powerline major modes is replaced by this function. +FACE is the face to use. +PAD is the padding around the minor modes. + +The original is called if `mode-icons-mode' is disabled. It is +saved in `mode-icons--real-powerline-major-mode'." + (if mode-icons-mode + (powerline-raw (format-mode-line (mode-icons--generate-major-mode-item face) face) face pad) + (mode-icons--real-powerline-major-mode face pad))) + (fset 'powerline-major-mode #'mode-icons--powerline-major-mode) + (fset 'mode-icons--real-powerline-raw #'powerline-raw) + (defun mode-icons--powerline-raw (str &optional face pad) + "Render STR as mode-line data using FACE and optionally PAD import on left (l) or right (r). +This uses `mode-icons--recolor-string' when `mode-icons-mode' is enabled." + (if mode-icons-mode + (when str + (let* ((rendered-str (format-mode-line str)) + (padded-str (concat + (when (and (> (length rendered-str) 0) (eq pad 'l)) " ") + (if (listp str) rendered-str str) + (when (and (> (length rendered-str) 0) (eq pad 'r)) " ")))) + (if face + (mode-icons--recolor-string (pl/add-text-property padded-str 'face face) + (mode-icons--selected-window-active) face) + padded-str))) + (mode-icons--real-powerline-raw str face pad))) + (fset 'powerline-raw #'mode-icons--powerline-raw))) + + +(eval-after-load 'emojify + '(progn + (mode-icons-reset-hash))) + +(provide 'mode-icons) +;;; mode-icons.el ends here +;; Local Variables: +;; indent-tabs-mode: nil +;; End: -- cgit v1.2.3