diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/emojify-20180611.1538/emojify.el')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/emojify-20180611.1538/emojify.el | 2122 |
1 files changed, 0 insertions, 2122 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/emojify-20180611.1538/emojify.el b/configs/shared/emacs/.emacs.d/elpa/emojify-20180611.1538/emojify.el deleted file mode 100644 index 1e0ccea0e5ea..000000000000 --- a/configs/shared/emacs/.emacs.d/elpa/emojify-20180611.1538/emojify.el +++ /dev/null @@ -1,2122 +0,0 @@ -;;; emojify.el --- Display emojis in Emacs -*- lexical-binding: t; -*- - -;; Copyright (C) 2015-2018 Iqbal Ansari - -;; Author: Iqbal Ansari <iqbalansari02@yahoo.com> -;; Keywords: multimedia, convenience -;; URL: https://github.com/iqbalansari/emacs-emojify -;; Version: 1.0 -;; Package-Requires: ((seq "1.11") (ht "2.0") (emacs "24.3")) - -;; 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 package displays emojis in Emacs similar to how Github, Slack etc do. It -;; can display plain ascii like ':)' as well as Github style emojis like ':smile:' -;; -;; It provides a minor mode `emojify-mode' to enable display of emojis in a buffer. -;; To enable emojify mode globally use `global-emojify-mode' -;; -;; For detailed documentation see the projects README file at -;; https://github.com/iqbalansari/emacs-emojify - - - -;;; Code: - -(require 'seq) -(require 'ht) - -(require 'subr-x nil :no-error) -(require 'cl-lib) -(require 'json) -(require 'regexp-opt) -(require 'jit-lock) -(require 'pcase) -(require 'tar-mode) -(require 'apropos) - - - -;; Satisfying the byte-compiler -;; We do not "require" these functions but if `org-mode' is active we use them - -;; Required to determine point is in an org-list -(declare-function org-list-get-item-begin "org-list") -(declare-function org-at-heading-p "org") - -;; Required to determine point is in an org-src block -(declare-function org-element-type "org-element") -(declare-function org-element-at-point "org-element") - -;; Required for integration with company-mode -(declare-function company-pseudo-tooltip-unhide "company") - -;; Shouldn't require 'jit-lock be enough :/ -(defvar jit-lock-start) -(defvar jit-lock-end) - -;; Used while inserting emojis using helm -(defvar helm-buffer) -(defvar helm-after-initialize-hook) - - - -;; Compatibility functions - -(defun emojify-user-error (format &rest args) - "Signal a pilot error, making a message by passing FORMAT and ARGS to ‘format-message’." - (if (fboundp 'user-error) - (apply #'user-error format args) - (apply #'error format args))) - -(defun emojify-face-height (face) - "Get font height for the FACE." - (let ((face-font (face-font face))) - (cond - ((and (display-multi-font-p) - ;; Avoid calling font-info if the frame's default font was - ;; not changed since the frame was created. That's because - ;; font-info is expensive for some fonts, see bug #14838. - (not (string= (frame-parameter nil 'font) face-font))) - (aref (font-info face-font) 3)) - (t (frame-char-height))))) - -(defun emojify-default-font-height () - "Return the height in pixels of the current buffer's default face font. - -`default-font-height' seems to be available only on Emacs versions after 24.3. -This provides a compatibility version for previous versions." - (if (fboundp 'default-font-height) - (default-font-height) - (emojify-face-height 'default))) - -(defun emojify-overlays-at (pos &optional sorted) - "Return a list of the overlays that contain the character at POS. -If SORTED is non-nil, then sort them by decreasing priority. - -The SORTED argument was introduced in Emacs 24.4, along with the incompatible -change that overlay priorities can be any Lisp object (earlier they were -restricted to integer and nil). This version uses the SORTED argument of -`overlays-at' on Emacs version 24.4 onwards and manually sorts the overlays by -priority on lower versions." - (if (version< emacs-version "24.4") - (let ((overlays-at-pos (overlays-at pos))) - (if sorted - (seq-sort (lambda (overlay1 overlay2) - (if (and (overlay-get overlay2 'priority) - (overlay-get overlay1 'priority)) - ;; If both overlays have priorities compare them - (< (overlay-get overlay1 'priority) - (overlay-get overlay2 'priority)) - ;; Otherwise overlay with nil priority is sorted below - ;; the one with integer value otherwise preserve order - (not (overlay-get overlay1 'priority)))) - overlays-at-pos) - overlays-at-pos)) - (overlays-at pos sorted))) - -(defun emojify--string-join (strings &optional separator) - "Join all STRINGS using SEPARATOR. - -This function is available on Emacs v24.4 and higher, it has been -backported here for compatibility with older Emacsen." - (if (fboundp 'string-join) - (apply #'string-join (list strings separator)) - (mapconcat 'identity strings separator))) - - - -;; Debugging helpers - -(define-minor-mode emojify-debug-mode - "Enable debugging for emojify-mode. - -By default emojify silences any errors during emoji redisplay. This is done -since emojis are redisplayed using jit-lock (the same mechanism used for -font-lock) as such any bugs in the code can cause other important things to -fail. This also turns on jit-debug-mode so that (e)debugging emojify's redisplay -functions work." - :init-value nil - (if emojify-debug-mode - (when (fboundp 'jit-lock-debug-mode) - (jit-lock-debug-mode +1)) - (when (fboundp 'jit-lock-debug-mode) - (jit-lock-debug-mode -1)))) - -(defmacro emojify-execute-ignoring-errors-unless-debug (&rest forms) - "Execute FORMS ignoring errors unless variable `emojify-debug-mode' is non-nil." - (declare (debug t) (indent 0)) - `(if emojify-debug-mode - (progn - ,@forms) - (ignore-errors - ,@forms))) - - - -;; Utility functions - -;; These should be bound dynamically by functions calling -;; `emojify--inside-rectangle-selection-p' and -;; `emojify--inside-non-rectangle-selection-p' to region-beginning and -;; region-end respectively. This is needed mark the original region which is -;; impossible to get after point moves during processing. -(defvar emojify-region-beg nil) -(defvar emojify-region-end nil) - -;; This should be bound dynamically to the location of point before emojify's -;; display loop, this since getting the point after point moves during -;; processing is impossible -(defvar emojify-current-point nil) - -(defmacro emojify-with-saved-buffer-state (&rest forms) - "Execute FORMS saving current buffer state. - -This saves point and mark, `match-data' and buffer modification state it also -inhibits buffer change, point motion hooks." - (declare (debug t) (indent 0)) - `(let ((inhibit-point-motion-hooks t) - (emojify-current-point (point)) - (emojify-region-beg (when (region-active-p) (region-beginning))) - (emojify-region-end (when (region-active-p) (region-end)))) - (with-silent-modifications - (save-match-data - (save-excursion - (save-restriction - (widen) - ,@forms)))))) - -(defmacro emojify-do-for-emojis-in-region (beg end &rest forms) - "For all emojis between BEG and END, execute the given FORMS. - -During the execution `emoji-start' and `emoji-end' are bound to the start -and end of the emoji for which the form is being executed." - (declare (debug t) (indent 2)) - `(let ((--emojify-loop-current-pos ,beg) - (--emojify-loop-end ,end) - emoji-start) - (while (and (> --emojify-loop-end --emojify-loop-current-pos) - (setq emoji-start (text-property-any --emojify-loop-current-pos --emojify-loop-end 'emojified t))) - (let ((emoji-end (+ emoji-start - (length (get-text-property emoji-start 'emojify-text))))) - ,@forms - (setq --emojify-loop-current-pos emoji-end))))) - -(defun emojify-message (format-string &rest args) - "Log debugging messages to buffer named 'emojify-log'. - -This is a substitute to `message' since using it during redisplay causes errors. -FORMAT-STRING and ARGS are same as the arguments to `message'." - (when emojify-debug-mode - (emojify-with-saved-buffer-state - (with-current-buffer (get-buffer-create "emojify-log") - (goto-char (point-max)) - (insert (apply #'format format-string args)) - (insert "\n"))))) - -(defun emojify--get-relevant-region () - "Try getting region in buffer that completely covers the current window. - -This is used instead of directly using `window-start' and `window-end', since -they return the values corresponding buffer in currently selected window, which -is incorrect if the buffer where there are called is not actually the buffer -visible in the selected window." - (let* ((window-size (- (window-end) (window-start))) - (start (max (- (point) window-size) (point-min))) - (end (min (+ (point) window-size) (point-max)))) - (cons start end))) - -(defun emojify-quit-buffer () - "Hide the current buffer. -There are windows other than the one the current buffer is displayed in quit the -current window too." - (interactive) - (if (= (length (window-list)) 1) - (bury-buffer) - (quit-window))) - -(defvar emojify-common-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "q" #'emojify-quit-buffer) - (define-key map "n" #'next-line) - (define-key map "p" #'previous-line) - (define-key map "r" #'isearch-backward) - (define-key map "s" #'isearch-forward) - (define-key map ">" #'end-of-buffer) - (define-key map "<" #'beginning-of-buffer) - - (dolist (key '("?" "h" "H")) - (define-key map key #'describe-mode)) - - (dolist (number (number-sequence 0 9)) - (define-key map (number-to-string number) #'digit-argument)) - - map) - "Common keybindings available in all special emojify buffers.") - - - -;; Customizations for control how emojis are displayed - -(defgroup emojify nil - "Customization options for emojify" - :group 'display - :prefix "emojify-") - -(defcustom emojify-emoji-json - (expand-file-name "data/emoji.json" - (cond (load-file-name (file-name-directory load-file-name)) - ((locate-library "emojify") (file-name-directory (locate-library "emojify"))) - (t default-directory))) - "The path to JSON file containing the configuration for displaying emojis." - :type 'file - :group 'emojify) - -(defvar emojify-emoji-set-json - (let ((json-array-type 'list) - (json-object-type 'hash-table)) - (json-read-file (expand-file-name "data/emoji-sets.json" - (cond (load-file-name (file-name-directory load-file-name)) - ((locate-library "emojify") (file-name-directory (locate-library "emojify"))) - (t default-directory)))))) - -(defcustom emojify-emoji-set "emojione-v2.2.6-22" - "The emoji set used to display emojis." - :type (append '(radio :tag "Emoji set") - (mapcar (lambda (set) (list 'const set)) - (ht-keys emojify-emoji-set-json))) - :group 'emojify) - -(defcustom emojify-emojis-dir - (locate-user-emacs-file "emojis") - "Path to the directory containing the emoji images." - :type 'directory - :group 'emojify) - -(defcustom emojify-display-style - 'image - "How the emoji's be displayed. - -Possible values are -`image' - Display emojis using images, this requires images are supported by - user's Emacs installation -`unicode' - Display emojis using unicode characters, this works well on - platforms with good emoji fonts. In this case the emoji text - ':wink:' will be substituted with 😉. -`ascii' - Display emojis as ascii characters, this is simplest and does not - require any external dependencies. In this cases emoji text like - ':wink:' are substituted with ascii equivalents like ';)'" - :type '(radio :tag "Emoji display style" - (const :tag "Display emojis as images" image) - (const :tag "Display emojis as unicode characters" unicode) - (const :tag "Display emojis as ascii string" ascii)) - :group 'emojify) - - - -;; Customizations to control the enabling of emojify-mode - -(defcustom emojify-inhibit-major-modes - '(dired-mode - doc-view-mode - debugger-mode - pdf-view-mode - image-mode - help-mode - ibuffer-mode - magit-popup-mode - magit-diff-mode - ert-results-mode - compilation-mode - proced-mode - mu4e-headers-mode) - "Major modes where emojify mode should not be enabled." - :type '(repeat symbol) - :group 'emojify) - -(defcustom emojify-inhibit-in-buffer-functions - '(emojify-minibuffer-p emojify-helm-buffer-p) - "Functions used inhibit emojify-mode in a buffer. - -These functions are called with one argument, the buffer where command -‘emojify-mode’ is about to be enabled, emojify is not enabled if any of the -functions return a non-nil value." - :type 'hook - :group 'emojify) - -(defvar emojify-inhibit-emojify-in-current-buffer-p nil - "Should emojify be inhibited in current buffer. - -This is a buffer local variable that can be set to inhibit enabling of -emojify in a buffer.") -(make-variable-buffer-local 'emojify-inhibit-emojify-in-current-buffer-p) - -(defvar emojify-minibuffer-reading-emojis-p nil - "Are we currently reading emojis using minibuffer?") - -(defun emojify-ephemeral-buffer-p (buffer) - "Determine if BUFFER is an ephemeral/temporary buffer." - (and (not (minibufferp)) - (string-match-p "^ " (buffer-name buffer)))) - -(defun emojify-inhibit-major-mode-p (buffer) - "Determine if user has disabled the `major-mode' enabled for the BUFFER. - -Returns non-nil if the buffer's major mode is part of `emojify-inhibit-major-modes'" - (with-current-buffer buffer - (apply #'derived-mode-p emojify-inhibit-major-modes))) - -(defun emojify-helm-buffer-p (buffer) - "Determine if the current BUFFER is a helm buffer." - (unless emojify-minibuffer-reading-emojis-p - (string-match-p "\\*helm" (buffer-name buffer)))) - -(defun emojify-minibuffer-p (buffer) - "Determine if the current BUFFER is a minibuffer." - (unless emojify-minibuffer-reading-emojis-p - (minibufferp buffer))) - -(defun emojify-buffer-p (buffer) - "Determine if `emojify-mode' should be enabled for given BUFFER. - -`emojify-mode' mode is not enabled in temporary buffers. Additionally user -can customize `emojify-inhibit-major-modes' and -`emojify-inhibit-in-buffer-functions' to disabled emojify in additional buffers." - (not (or emojify-inhibit-emojify-in-current-buffer-p - (emojify-ephemeral-buffer-p (current-buffer)) - (emojify-inhibit-major-mode-p (current-buffer)) - (buffer-base-buffer buffer) - (run-hook-with-args-until-success 'emojify-inhibit-in-buffer-functions buffer)))) - - - -;; Customizations to control display of emojis - -(defvar emojify-emoji-style-change-hook nil - "Hooks run when emoji style changes.") - -;;;###autoload -(defun emojify-set-emoji-styles (styles) - "Set the type of emojis that should be displayed. - -STYLES is the styles emoji styles that should be used, see `emojify-emoji-styles'" - (when (not (listp styles)) - (setq styles (list styles)) - (warn "`emojify-emoji-style' has been deprecated use `emojify-emoji-styles' instead!")) - - (setq-default emojify-emoji-styles styles) - - (run-hooks 'emojify-emoji-style-change-hook)) - -(defcustom emojify-emoji-styles - '(ascii unicode github) - "The type of emojis that should be displayed. - -These can have one of the following values - -`ascii' - Display only ascii emojis for example ';)' -`unicode' - Display only unicode emojis for example '😉' -`github' - Display only github style emojis for example ':wink:'" - :type '(set - (const :tag "Display only ascii emojis" ascii) - (const :tag "Display only github emojis" github) - (const :tag "Display only unicode codepoints" unicode)) - :set (lambda (_ value) (emojify-set-emoji-styles value)) - :group 'emojify) - -(defcustom emojify-program-contexts - '(comments string code) - "Contexts where emojis can be displayed in programming modes. - -Possible values are -`comments' - Display emojis in comments -`string' - Display emojis in strings -`code' - Display emojis in code (this is applicable only for unicode emojis)" - :type '(set :tag "Contexts where emojis should be displayed in programming modes" - (const :tag "Display emojis in comments" comments) - (const :tag "Display emojis in string" string) - (const :tag "Display emojis in code" code)) - :group 'emojify) - -(defcustom emojify-inhibit-functions - '(emojify-in-org-tags-p emojify-in-org-list-p) - "Functions used to determine given emoji should displayed at current point. - -These functions are called with 3 arguments, the text to be emojified, the start -of emoji text and the end of emoji text. These functions are called with the -buffer where emojis are going to be displayed selected." - :type 'hook - :group 'emojify) - -(defcustom emojify-composed-text-p t - "Should composed text be emojified." - :type 'boolean - :group 'emojify) - -(defcustom emojify-company-tooltips-p t - "Should company mode tooltips be emojified." - :type 'boolean - :group 'emojify) - -(defun emojify-in-org-tags-p (match beg _end) - "Determine whether the point is on `org-mode' tag. - -MATCH, BEG and _END are the text currently matched emoji and the start position -and end position of emoji text respectively. - -Easiest would have to inspect face at point but unfortunately, there is no -way to guarantee that we run after font-lock" - (and (memq major-mode '(org-mode org-agenda-mode)) - (string-match-p ":[^:]+[:]?" match) - (org-at-heading-p) - (save-excursion - (save-match-data - (goto-char beg) - (looking-at ":[^:]+:[\s-]*$"))))) - -(defun emojify-in-org-list-p (text beg &rest ignored) - "Determine whether the point is in `org-mode' list. - -TEXT is the text which is supposed to rendered a an emoji. BEG is the beginning -of the emoji text in the buffer. The arguments IGNORED are ignored." - (and (eq major-mode 'org-mode) - (equal text "8)") - (equal (org-list-get-item-begin) beg))) - -(defun emojify-valid-program-context-p (emoji beg end) - "Determine if EMOJI should be displayed for text between BEG and END. - -This returns non-nil if the region is valid according to `emojify-program-contexts'" - (when emojify-program-contexts - (let* ((syntax-beg (syntax-ppss beg)) - (syntax-end (syntax-ppss end)) - (context (cond ((and (nth 3 syntax-beg) - (nth 3 syntax-end)) - 'string) - ((and (nth 4 syntax-beg) - (nth 4 syntax-end)) - 'comments) - (t 'code)))) - (and (memql context emojify-program-contexts) - (if (equal context 'code) - (and (string= (ht-get emoji "style") "unicode") - (memql 'unicode emojify-emoji-styles)) - t))))) - -(defun emojify-inside-org-src-p (point) - "Return non-nil if POINT is inside `org-mode' src block. - -This is used to inhibit display of emoji's in `org-mode' src blocks -since our mechanisms do not work in it." - (when (eq major-mode 'org-mode) - (save-excursion - (goto-char point) - (eq (org-element-type (org-element-at-point)) 'src-block)))) - -(defun emojify-looking-at-end-of-list-maybe (point) - "Determine if POINT is end of a list. - -This is not accurate since it restricts the region to scan to -the visible area." - (let* ((area (emojify--get-relevant-region)) - (beg (car area)) - (end (cdr area))) - (save-restriction - (narrow-to-region beg end) - (let ((list-start (ignore-errors (scan-sexps point -1)))) - (when (and list-start - ;; Ignore the starting brace if it is an emoji - (not (get-text-property list-start 'emojified))) - ;; If we got a list start make sure both start and end - ;; belong to same string/comment - (let ((syntax-beg (syntax-ppss list-start)) - (syntax-end (syntax-ppss point))) - (and list-start - (eq (nth 8 syntax-beg) - (nth 8 syntax-end))))))))) - -(defun emojify-valid-ascii-emoji-context-p (beg end) - "Determine if the okay to display ascii emoji between BEG and END." - ;; The text is at the start of the buffer - (and (or (not (char-before beg)) - ;; 32 space since ? (? followed by a space) is not readable - ;; 34 is " since?" confuses font-lock - ;; 41 is ) since?) (extra paren) confuses most packages - (memq (char-syntax (char-before beg)) - ;; space - '(32 - ;; start/end of string - 34 - ;; whitespace syntax - ?- - ;; comment start - ?< - ;; comment end, this handles text at start of line immediately - ;; after comment line in a multiline comment - ?>))) - ;; The text is at the end of the buffer - (or (not (char-after end)) - (memq (char-syntax (char-after end)) - ;; space - '(32 - ;; start/end of string - 34 - ;; whitespace syntax - ?- - ;; punctuation - ?. - ;; closing braces - 41 - ;; comment end - ?>))))) - - - -;; Obsolete vars - -(define-obsolete-variable-alias 'emojify-emoji-style 'emojify-emoji-styles "0.2") -(define-obsolete-function-alias 'emojify-set-emoji-style 'emojify-set-emoji-styles "0.2") - - - -;; Customizations to control the behaviour when point enters emojified text - -(defcustom emojify-point-entered-behaviour 'echo - "The behaviour when point enters, an emojified text. - -It can be one of the following -`echo' - Echo the underlying text in the minibuffer -`uncover' - Display the underlying text while point is on it -function - It is called with 2 arguments (the buffer where emoji appears is - current during execution) - 1) starting position of emoji text - 2) ending position of emoji text - -Does nothing if the value is anything else." - ;; TODO: Mention custom function - :type '(radio :tag "Behaviour when point enters an emoji" - (const :tag "Echo the underlying emoji text in the minibuffer" echo) - (const :tag "Uncover (undisplay) the underlying emoji text" uncover)) - :group 'emojify) - -(defcustom emojify-reveal-on-isearch t - "Should underlying emoji be displayed when point enters emoji while in isearch mode.") - -(defcustom emojify-show-help t - "If non-nil the underlying text is displayed in a popup when mouse moves over it." - :type 'boolean - :group 'emojify) - -(defun emojify-on-emoji-enter (beginning end) - "Executed when point enters emojified text between BEGINNING and END." - (cond ((and (eq emojify-point-entered-behaviour 'echo) - ;; Do not echo in isearch-mode - (not isearch-mode) - (not (active-minibuffer-window)) - (not (current-message))) - (message (substring-no-properties (get-text-property beginning 'emojify-text)))) - ((eq emojify-point-entered-behaviour 'uncover) - (put-text-property beginning end 'display nil)) - ((functionp 'emojify-point-entered-behaviour) - (funcall emojify-point-entered-behaviour beginning end))) - - (when (and isearch-mode emojify-reveal-on-isearch) - (put-text-property beginning end 'display nil))) - -(defun emojify-on-emoji-exit (beginning end) - "Executed when point exits emojified text between BEGINNING and END." - (put-text-property beginning - end - 'display - (get-text-property beginning 'emojify-display))) - -(defvar-local emojify--last-emoji-pos nil) - -(defun emojify-detect-emoji-entry/exit () - "Detect emoji entry and exit and run appropriate handlers. - -This is inspired by `prettify-symbol-mode's logic for -`prettify-symbols-unprettify-at-point'." - (while-no-input - (emojify-with-saved-buffer-state - (when emojify--last-emoji-pos - (emojify-on-emoji-exit (car emojify--last-emoji-pos) (cdr emojify--last-emoji-pos))) - - (when (get-text-property (point) 'emojified) - (let* ((text-props (text-properties-at (point))) - (buffer (plist-get text-props 'emojify-buffer)) - (match-beginning (plist-get text-props 'emojify-beginning)) - (match-end (plist-get text-props 'emojify-end))) - (when (eq buffer (current-buffer)) - (emojify-on-emoji-enter match-beginning match-end) - (setq emojify--last-emoji-pos (cons match-beginning match-end)))))))) - -(defun emojify-help-function (_window _string pos) - "Function to get help string to be echoed when point/mouse into the point. - -To understand WINDOW, STRING and POS see the function documentation for -`help-echo' text-property." - (when (and emojify-show-help - (not isearch-mode) - (not (active-minibuffer-window)) - (not (current-message))) - (plist-get (text-properties-at pos) 'emojify-text))) - - - -;; Core functions and macros - -;; Variables related to user emojis - -(defcustom emojify-user-emojis nil - "User specified custom emojis. - -This is an alist where first element of cons is the text to be displayed as -emoji, while the second element of the cons is an alist containing data about -the emoji. - -The inner alist should have atleast (not all keys are strings) - -`name' - The name of the emoji -`style' - This should be one of \"github\", \"ascii\" or \"github\" - (see `emojify-emoji-styles') - -The alist should contain one of (see `emojify-display-style') -`unicode' - The replacement for the provided emoji for \"unicode\" display style -`image' - The replacement for the provided emoji for \"image\" display style. - This should be the absolute path to the image -`ascii' - The replacement for the provided emoji for \"ascii\" display style - -Example - -The following assumes that custom images are at ~/.emacs.d/emojis/trollface.png and -~/.emacs.d/emojis/neckbeard.png - -'((\":troll:\" . ((\"name\" . \"Troll\") - (\"image\" . \"~/.emacs.d/emojis/trollface.png\") - (\"style\" . \"github\"))) - (\":neckbeard:\" . ((\"name\" . \"Neckbeard\") - (\"image\" . \"~/.emacs.d/emojis/neckbeard.png\") - (\"style\" . \"github\"))))") - -(defvar emojify--user-emojis nil - "User specified custom emojis.") - -(defvar emojify--user-emojis-regexp nil - "Regexp to match user specified custom emojis.") - -;; Variables related to default emojis -(defvar emojify-emojis nil - "Data about the emojis, this contains only the emojis that come with emojify.") - -(defvar emojify-regexps nil - "List of regexps to match text to be emojified.") - -(defvar emojify--completing-candidates-cache nil - "Cached values for completing read candidates calculated for `emojify-completing-read'.") - -;; Cache for emoji completing read candidates -(defun emojify--get-completing-read-candidates () - "Get the candidates to be used for `emojify-completing-read'. - -The candidates are calculated according to currently active -`emojify-emoji-styles' and cached" - (let ((styles (mapcar #'symbol-name emojify-emoji-styles))) - (unless (and emojify--completing-candidates-cache - (equal styles (car emojify--completing-candidates-cache))) - (setq emojify--completing-candidates-cache - (cons styles - (let ((emojis (ht-create #'equal))) - (emojify-emojis-each (lambda (key value) - (when (seq-position styles (ht-get value "style")) - (ht-set! emojis - (format "%s - %s (%s)" - key - (ht-get value "name") - (ht-get value "style")) - value)))) - emojis)))) - (cdr emojify--completing-candidates-cache))) - -(defun emojify-create-emojify-emojis (&optional force) - "Create `emojify-emojis' if needed. - -The function avoids reading emoji data if it has already been read unless FORCE -in which case emoji data is re-read." - (when (or force (not emojify-emojis)) - (emojify-set-emoji-data))) - -(defun emojify-get-emoji (emoji) - "Get data for given EMOJI. - -This first looks for the emoji in `emojify--user-emojis', -and then in `emojify-emojis'." - (or (when emojify--user-emojis - (ht-get emojify--user-emojis emoji)) - (ht-get emojify-emojis emoji))) - -(defun emojify-emojis-each (function) - "Execute FUNCTION for each emoji. - -This first runs function for `emojify--user-emojis', -and then `emojify-emojis'." - (when emojify--user-emojis - (ht-each function emojify--user-emojis)) - (ht-each function emojify-emojis)) - -(defun emojify--verify-user-emojis (emojis) - "Verify the EMOJIS in correct user format." - (seq-every-p (lambda (emoji) - (and (assoc "name" (cdr emoji)) - ;; Make sure style is present is only one of - ;; "unicode", "ascii" and "github". - (assoc "style" (cdr emoji)) - (seq-position '("unicode" "ascii" "github") - (cdr (assoc "style" (cdr emoji)))) - (or (assoc "unicode" (cdr emoji)) - (assoc "image" (cdr emoji)) - (assoc "ascii" (cdr emoji))))) - emojis)) - -(defun emojify-set-emoji-data () - "Read the emoji data for STYLES and set the regexp required to search them." - (setq-default emojify-emojis (let ((json-array-type 'list) - (json-object-type 'hash-table)) - (json-read-file emojify-emoji-json))) - - (let (unicode-emojis ascii-emojis) - (ht-each (lambda (emoji data) - (when (string= (ht-get data "style") "unicode") - (push emoji unicode-emojis)) - - (when (string= (ht-get data "style") "ascii") - (push emoji ascii-emojis))) - emojify-emojis) - - ;; Construct emojify-regexps such that github style are searched first - ;; followed by unicode and then ascii emojis. - (setq emojify-regexps (list ":[[:alnum:]+_-]+:" - (regexp-opt unicode-emojis) - (regexp-opt ascii-emojis)))) - - (when emojify-user-emojis - (if (emojify--verify-user-emojis emojify-user-emojis) - ;; Create entries for user emojis - (let ((emoji-pairs (mapcar (lambda (user-emoji) - (cons (car user-emoji) - (ht-from-alist (cdr user-emoji)))) - emojify-user-emojis))) - (setq emojify--user-emojis (ht-from-alist emoji-pairs)) - (setq emojify--user-emojis-regexp (regexp-opt (mapcar #'car emoji-pairs)))) - (message "[emojify] User emojis are not in correct format ignoring them."))) - - (emojify-emojis-each (lambda (emoji data) - ;; Add the emoji text to data, this makes the values - ;; of the `emojify-emojis' standalone containing all - ;; data about the emoji - (ht-set! data "emoji" emoji) - (ht-set! data "custom" (and emojify--user-emojis - (ht-get emojify--user-emojis emoji))))) - - ;; Clear completion candidates cache - (setq emojify--completing-candidates-cache nil)) - -(defvar emojify-emoji-keymap - (let ((map (make-sparse-keymap))) - (define-key map [remap delete-char] #'emojify-delete-emoji-forward) - (define-key map [remap delete-forward-char] #'emojify-delete-emoji-forward) - (define-key map [remap backward-delete-char] #'emojify-delete-emoji-backward) - (define-key map [remap org-delete-backward-char] #'emojify-delete-emoji-backward) - (define-key map [remap delete-backward-char] #'emojify-delete-emoji-backward) - (define-key map [remap backward-delete-char-untabify] #'emojify-delete-emoji-backward) - map)) - -(defun emojify-image-dir () - "Get the path to directory containing images for currently selected emoji set." - (expand-file-name emojify-emoji-set - emojify-emojis-dir)) - -(defun emojify--get-point-col-and-line (point) - "Return a cons of containing the column number and line at POINT." - (save-excursion - (goto-char point) - (cons (current-column) (line-number-at-pos)))) - -(defun emojify--get-composed-text (point) - "Get the text used as composition property at POINT. - -This does not check if there is composition property at point the callers should -make sure the point has a composition property otherwise this function will -fail." - (emojify--string-join (mapcar #'char-to-string - (decode-composition-components (nth 2 - (find-composition point - nil - nil - t)))))) - -(defun emojify--inside-rectangle-selection-p (beg end) - "Check if region marked by BEG and END is inside a rectangular selection. - -In addition to explicit the parameters BEG and END, calling functions should -also dynamically bind `emojify-region-beg' and `emojify-region-end' to beginning -and end of region respectively." - (when (and emojify-region-beg - (bound-and-true-p rectangle-mark-mode)) - (let ((rect-beg (emojify--get-point-col-and-line emojify-region-beg)) - (rect-end (emojify--get-point-col-and-line emojify-region-end)) - (emoji-start-pos (emojify--get-point-col-and-line beg)) - (emoji-end-pos (emojify--get-point-col-and-line end))) - (or (and (<= (car rect-beg) (car emoji-start-pos)) - (<= (car emoji-start-pos) (car rect-end)) - (<= (cdr rect-beg) (cdr emoji-start-pos)) - (<= (cdr emoji-start-pos) (cdr rect-end))) - (and (<= (car rect-beg) (car emoji-end-pos)) - (<= (car emoji-end-pos) (car rect-end)) - (<= (cdr rect-beg) (cdr emoji-end-pos)) - (<= (cdr emoji-end-pos) (cdr rect-end))))))) - -(defun emojify--inside-non-rectangle-selection-p (beg end) - "Check if region marked by BEG and END is inside a non-regular selection. - -In addition to the explicit parameters BEG and END, calling functions should -also dynamically bind `emojify-region-beg' and `emojify-region-end' to beginning -and end of region respectively." - (when (and emojify-region-beg - (region-active-p) - (not (bound-and-true-p rectangle-mark-mode))) - (or (and (< emojify-region-beg beg) - (<= beg emojify-region-end)) - (and (< emojify-region-beg end) - (<= end emojify-region-end))))) - -(defun emojify--region-background-maybe (beg end) - "If the BEG and END falls inside an active region return the region face. - -This returns nil if the emojis between BEG and END do not fall in region." - ;; `redisplay-highlight-region-function' was not defined in Emacs 24.3 - (when (and (or (not (boundp 'redisplay-highlight-region-function)) - (equal (default-value 'redisplay-highlight-region-function) redisplay-highlight-region-function)) - (or (emojify--inside-non-rectangle-selection-p beg end) - (emojify--inside-rectangle-selection-p beg end))) - (face-background 'region))) - -(defun emojify--get-image-background (beg end) - "Get the color to be used as background for emoji between BEG and END." - ;; We do a separate check for region since `background-color-at-point' - ;; does not always detect background color inside regions properly - (or (emojify--region-background-maybe beg end) - (save-excursion - (goto-char beg) - (background-color-at-point)))) - -(defvar emojify--imagemagick-support-cache (ht-create)) - -(defun emojify--imagemagick-supports-p (format) - "Check if imagemagick support given FORMAT. - -This function caches the result of the check since the naive check - - (memq format (imagemagick-types)) - -can be expensive if imagemagick-types returns a large list, this is -especially problematic since this check is potentially called during -very redisplay. See https://github.com/iqbalansari/emacs-emojify/issues/41" - (when (fboundp 'imagemagick-types) - (when (equal (ht-get emojify--imagemagick-support-cache format 'unset) 'unset) - (ht-set emojify--imagemagick-support-cache format (memq format (imagemagick-types)))) - (ht-get emojify--imagemagick-support-cache format))) - -(defun emojify--get-image-display (data buffer beg end &optional target) - "Get the display text property to display the emoji as an image. - -DATA holds the emoji data, _BUFFER is the target buffer where the emoji is to be -displayed, BEG and END delimit the region where emoji will be displayed. For -explanation of TARGET see the documentation of `emojify--get-text-display-props'. - -TODO: Perhaps TARGET should be generalized to work with overlays, buffers and -other different display constructs, for now this works." - (when (ht-get data "image") - (let* ((image-file (expand-file-name (ht-get data "image") - (emojify-image-dir))) - (image-type (intern (upcase (file-name-extension image-file))))) - (when (file-exists-p image-file) - (create-image image-file - ;; use imagemagick if available and supports PNG images - ;; (allows resizing images) - (when (emojify--imagemagick-supports-p image-type) - 'imagemagick) - nil - :ascent 'center - :heuristic-mask t - :background (cond ((equal target 'mode-line) - (face-background 'mode-line nil 'default)) - (t (emojify--get-image-background beg end))) - ;; no-op if imagemagick is not available - :height (cond ((bufferp target) - (with-current-buffer target - (emojify-default-font-height))) - ((equal target 'mode-line) - (emojify-face-height 'mode-line)) - (t (with-current-buffer buffer - (emojify-default-font-height))))))))) - -(defun emojify--get-unicode-display (data &rest ignored) - "Get the display text property to display the emoji as an unicode character. - -DATA holds the emoji data, rest of the arguments IGNORED are ignored" - (let* ((unicode (ht-get data "unicode")) - (characters (when unicode - (string-to-vector unicode)))) - (when (seq-every-p #'char-displayable-p characters) - unicode))) - -(defun emojify--get-ascii-display (data &rest ignored) - "Get the display text property to display the emoji as an ascii characters. - -DATA holds the emoji data, rest of the arguments IGNORED are ignored." - (ht-get data "ascii")) - -(defun emojify--get-text-display-props (emoji buffer beg end &optional target) - "Get the display property for an EMOJI. - -BUFFER is the buffer currently holding the EMOJI, BEG and END delimit the region -containing the emoji. TARGET can either be a buffer object or a special value -mode-line. It is used to indicate where EMOJI would be displayed, properties -like font-height are inherited from TARGET if provided." - (funcall (pcase emojify-display-style - (`image #'emojify--get-image-display) - (`unicode #'emojify--get-unicode-display) - (`ascii #'emojify--get-ascii-display)) - emoji - buffer - beg - end - target)) - -(defun emojify--propertize-text-for-emoji (emoji text buffer start end &optional target) - "Display EMOJI for TEXT in BUFFER between START and END. - -For explanation of TARGET see the documentation of -`emojify--get-text-display-props'." - (let ((display-prop - (emojify--get-text-display-props emoji buffer start end target)) - (buffer-props (unless target - (list 'emojify-buffer buffer - 'emojify-beginning (copy-marker start) - 'emojify-end (copy-marker end) - 'yank-handler (list nil text) - 'keymap emojify-emoji-keymap - 'help-echo #'emojify-help-function)))) - (when display-prop - (add-text-properties start - end - (append (list 'emojified t - 'emojify-display display-prop - 'display display-prop - 'emojify-text text) - buffer-props))))) - -(defun emojify-display-emojis-in-region (beg end) - "Display emojis in region. - -BEG and END are the beginning and end of the region respectively. - -Displaying happens in two phases, first search based phase displays actual text -appearing in buffer as emojis. In the next phase composed text is searched for -emojis and displayed. - -A minor problem here is that if the text is composed after this display loop it -would not be displayed as emoji, although in practice the two packages that use -the composition property `prettify-symbol-mode' and `org-bullets' use the -font-lock machinery which runs before emojify's display loop, so hopefully this -should not be a problem 🤞." - (emojify-with-saved-buffer-state - ;; Make sure we halt if displaying emojis takes more than a second (this - ;; might be too large duration) - (with-timeout (1 (emojify-message "Failed to display emojis under 1 second")) - (seq-doseq (regexp (apply #'append - (when emojify--user-emojis-regexp - (list emojify--user-emojis-regexp)) - (list emojify-regexps))) - (let (case-fold-search) - (goto-char beg) - (while (and (> end (point)) - (search-forward-regexp regexp end t)) - (let* ((match-beginning (match-beginning 0)) - (match-end (match-end 0)) - (match (match-string-no-properties 0)) - (buffer (current-buffer)) - (emoji (emojify-get-emoji match)) - (force-display (get-text-property match-beginning 'emojify-force-display))) - (when (and emoji - (not (or (get-text-property match-beginning 'emojify-inhibit) - (get-text-property match-end 'emojify-inhibit))) - (memql (intern (ht-get emoji "style")) - emojify-emoji-styles) - ;; Skip displaying this emoji if the its bounds are - ;; already part of an existing emoji. Since the emojis - ;; are searched in descending order of length (see - ;; construction of emojify-regexp in `emojify-set-emoji-data'), - ;; this means larger emojis get precedence over smaller - ;; ones - (not (or (get-text-property match-beginning 'emojified) - (get-text-property (1- match-end) 'emojified))) - ;; Display unconditionally in non-prog mode - (or (not (derived-mode-p 'prog-mode 'tuareg--prog-mode 'comint-mode 'smalltalk-mode)) - ;; In prog mode enable respecting `emojify-program-contexts' - (emojify-valid-program-context-p emoji match-beginning match-end)) - - ;; Display ascii emojis conservatively, since they have potential - ;; to be annoying consider d: in head:, except while executing apropos - ;; emoji - (or (not (string= (ht-get emoji "style") "ascii")) - force-display - (emojify-valid-ascii-emoji-context-p match-beginning match-end)) - - (or force-display - (not (emojify-inside-org-src-p match-beginning))) - - ;; Inhibit possibly inside a list - ;; 41 is ?) but packages get confused by the extra closing paren :) - ;; TODO Report bugs to such packages - (or force-display - (not (and (eq (char-syntax (char-before match-end)) 41) - (emojify-looking-at-end-of-list-maybe match-end)))) - - (not (run-hook-with-args-until-success 'emojify-inhibit-functions match match-beginning match-end))) - (emojify--propertize-text-for-emoji emoji match buffer match-beginning match-end))) - ;; Stop a bit to let `with-timeout' kick in - (sit-for 0 t)))) - - ;; Loop to emojify composed text - (when (and emojify-composed-text-p - ;; Skip this if user has disabled unicode style emojis, since - ;; we display only composed text that are unicode emojis - (memql 'unicode emojify-emoji-styles)) - (goto-char beg) - (let ((compose-start (if (get-text-property beg 'composition) - ;; Check `beg' first for composition property - ;; since `next-single-property-change' will - ;; search for region after `beg' for property - ;; change thus skipping any composed text at - ;; `beg' - beg - (next-single-property-change beg - 'composition - nil - end)))) - (while (and (> end (point)) - ;; `end' would be equal to `compose-start' if there was no - ;; text with composition found within `end', this happens - ;; because `next-single-property-change' returns the limit - ;; (and we use `end' as the limit) if no match is found - (> end compose-start) - compose-start) - (let* ((match (emojify--get-composed-text compose-start)) - (emoji (emojify-get-emoji match)) - (compose-end (next-single-property-change compose-start 'composition nil end))) - ;; Display only composed text that is unicode char - (when (and emoji - (string= (ht-get emoji "style") "unicode")) - (emojify--propertize-text-for-emoji emoji match (current-buffer) compose-start compose-end)) - ;; Setup the next loop - (setq compose-start (and compose-end (next-single-property-change compose-end - 'composition - nil - end))) - (goto-char compose-end)) - ;; Stop a bit to let `with-timeout' kick in - (sit-for 0 t))))))) - -(defun emojify-undisplay-emojis-in-region (beg end) - "Undisplay the emojis in region. - -BEG and END are the beginning and end of the region respectively" - (emojify-with-saved-buffer-state - (while (< beg end) - ;; Get the start of emojified region in the region, the region is marked - ;; with text-property `emojified' whose value is `t'. The region is marked - ;; so that we do not inadvertently remove display or other properties - ;; inserted by other packages. This might fail too if a package adds any - ;; of these properties between an emojified text, but that situation is - ;; hopefully very rare and this is better than blindly removing all text - ;; properties - (let* ((emoji-start (text-property-any beg end 'emojified t)) - ;; Get the end emojified text, if we could not find the start set - ;; emoji-end to region `end', this merely to make looping easier. - (emoji-end (or (and emoji-start - (text-property-not-all emoji-start end 'emojified t)) - ;; If the emojified text is at the end of the region - ;; assume that end is the emojified text. - end))) - ;; Proceed only if we got start of emojified text - (when emoji-start - ;; Remove the properties - (remove-text-properties emoji-start emoji-end (append (list 'emojified t - 'display t - 'emojify-display t - 'emojify-buffer t - 'emojify-text t - 'emojify-beginning t - 'emojify-end t - 'yank-handler t - 'keymap t - 'help-echo t - 'rear-nonsticky t)))) - ;; Setup the next iteration - (setq beg emoji-end))))) - -(defun emojify-redisplay-emojis-in-region (&optional beg end) - "Redisplay emojis in region between BEG and END. - -Redisplay emojis in the visible region if BEG and END are not specified" - (let* ((area (emojify--get-relevant-region)) - (beg (save-excursion - (goto-char (or beg (car area))) - (line-beginning-position))) - (end (save-excursion - (goto-char (or end (cdr area))) - (line-end-position)))) - (unless (> (- end beg) 5000) - (emojify-execute-ignoring-errors-unless-debug - (emojify-undisplay-emojis-in-region beg end) - (emojify-display-emojis-in-region beg end))))) - -(defun emojify-after-change-extend-region-function (beg end _len) - "Extend the region to be emojified. - -This simply extends the region to be fontified to the start of line at BEG and -end of line at END. _LEN is ignored. - -The idea is since an emoji cannot span multiple lines, redisplaying complete -lines ensures that all the possibly affected emojis are redisplayed." - (let ((emojify-jit-lock-start (save-excursion - (goto-char beg) - (line-beginning-position))) - (emojify-jit-lock-end (save-excursion - (goto-char end) - (line-end-position)))) - (setq jit-lock-start (if jit-lock-start - (min jit-lock-start emojify-jit-lock-start) - emojify-jit-lock-start)) - (setq jit-lock-end (if jit-lock-end - (max jit-lock-end emojify-jit-lock-end) - emojify-jit-lock-end)))) - - - -;; Emojify standalone strings - -(defun emojify-string (string &optional styles target) - "Create a propertized version of STRING, to display emojis belonging STYLES. - -TARGET can either be a buffer object or a special value mode-line. It is used -to indicate where EMOJI would be displayed, properties like font-height are -inherited from TARGET if provided. See also `emojify--get-text-display-props'." - (emojify-create-emojify-emojis) - (let ((target (or target (current-buffer)))) - (with-temp-buffer - (insert string) - (let ((beg (point-min)) - (end (point-max)) - (styles (or styles '(unicode)))) - (seq-doseq (regexp (apply #'append - (when emojify--user-emojis-regexp - (list emojify--user-emojis-regexp)) - (list emojify-regexps))) - (goto-char beg) - (while (and (> end (point)) - (search-forward-regexp regexp end t)) - (let* ((match-beginning (match-beginning 0)) - (match-end (match-end 0)) - (match (match-string-no-properties 0)) - (buffer (current-buffer)) - (emoji (emojify-get-emoji match))) - (when (and emoji - (not (or (get-text-property match-beginning 'emojify-inhibit) - (get-text-property match-end 'emojify-inhibit))) - (memql (intern (ht-get emoji "style")) styles) - ;; Skip displaying this emoji if the its bounds are - ;; already part of an existing emoji. Since the emojis - ;; are searched in descending order of length (see - ;; construction of emojify-regexp in `emojify-set-emoji-data'), - ;; this means larger emojis get precedence over smaller - ;; ones - (not (or (get-text-property match-beginning 'emojified) - (get-text-property (1- match-end) 'emojified)))) - (emojify--propertize-text-for-emoji emoji match buffer match-beginning match-end target)))))) - (buffer-string)))) - - - -;; Electric delete functionality - -(defun emojify--find-key-binding-ignoring-emojify-keymap (key) - "Find the binding for given KEY ignoring the text properties at point. - -This is needed since `key-binding' looks up in keymap text property as well -which is not what we want when falling back in `emojify-delete-emoji'" - (let* ((key-binding (or (minor-mode-key-binding key) - (local-key-binding key) - (global-key-binding key)))) - (when key-binding - (or (command-remapping key-binding - nil - (seq-filter (lambda (keymap) - (not (equal keymap emojify-emoji-keymap))) - (current-active-maps))) - key-binding)))) - -(defun emojify-delete-emoji (point) - "Delete emoji at POINT." - (if (get-text-property point 'emojified) - (delete-region (get-text-property point 'emojify-beginning) - (get-text-property point 'emojify-end)) - (call-interactively (emojify--find-key-binding-ignoring-emojify-keymap (this-command-keys))))) - -(defun emojify-delete-emoji-forward () - "Delete emoji after point." - (interactive) - (emojify-delete-emoji (point))) - -(defun emojify-delete-emoji-backward () - "Delete emoji before point." - (interactive) - (emojify-delete-emoji (1- (point)))) - -;; Integrate with delete-selection-mode -;; Basically instruct delete-selection mode to override our commands -;; if the region is active. -(put 'emojify-delete-emoji-forward 'delete-selection 'supersede) -(put 'emojify-delete-emoji-backward 'delete-selection 'supersede) - - - -;; Updating background color on selection - -(defun emojify--update-emojis-background-in-region (&optional beg end) - "Update the background color for emojis between BEG and END." - (when (equal emojify-display-style 'image) - (emojify-with-saved-buffer-state - (emojify-do-for-emojis-in-region beg end - (plist-put (cdr (get-text-property emoji-start 'display)) - :background - (emojify--get-image-background emoji-start - emoji-end)))))) - -(defun emojify--update-emojis-background-in-region-starting-at (point) - "Update background color for emojis in buffer starting at POINT. - -This updates the emojis in the region starting from POINT, the end of region is -determined by product of `frame-height' and `frame-width' which roughly -corresponds to the visible area. POINT usually corresponds to the starting -position of the window, see -`emojify-update-visible-emojis-background-after-command' and -`emojify-update-visible-emojis-background-after-window-scroll' - -NOTE: `window-text-height' and `window-text-width' would have been more -appropriate here however they were not defined in Emacs v24.3 and below." - (let* ((region-beginning point) - (region-end (min (+ region-beginning (* (frame-height) - (frame-width))) - (point-max)))) - (emojify--update-emojis-background-in-region region-beginning - region-end))) - -(defun emojify-update-visible-emojis-background-after-command () - "Function added to `post-command-hook' when region is active. - -This function updates the backgrounds of the emojis in the region changed after -the command. - -Ideally this would have been good enough to update emoji backgounds after region -changes, unfortunately this does not work well with commands that scroll the -window specifically `window-start' and `window-end' (sometimes only `window-end') -report incorrect values. - -To work around this -`emojify-update-visible-emojis-background-after-window-scroll' is added to -`window-scroll-functions' to update emojis on window scroll." - (while-no-input (emojify--update-emojis-background-in-region-starting-at (window-start)))) - -(defun emojify-update-visible-emojis-background-after-window-scroll (_window display-start) - "Function added to `window-scroll-functions' when region is active. - -This function updates the backgrounds of the emojis in the newly displayed area -of the window. DISPLAY-START corresponds to the new start of the window." - (while-no-input (emojify--update-emojis-background-in-region-starting-at display-start))) - - - -;; Lazy image downloading - -(defcustom emojify-download-emojis-p 'ask - "Should emojify download images, if the selected emoji sets are not available. - -Emojify can automatically download the images required to display the selected -emoji set. By default the user will be asked for confirmation before downloading -the image. Set this variable to t to download the images without asking for -confirmation. Setting it to nil will disable automatic download of the images. - -Please note that emojify will not download the images if Emacs is running in -non-interactive mode and `emojify-download-emojis-p' is set to `ask'." - :type '(radio :tag "Automatically download required images" - (const :tag "Ask before downloading" ask) - (const :tag "Download without asking" t) - (const :tag "Disable automatic downloads" nil)) - :group 'emojify) - -(defvar emojify--refused-image-download-p nil - "Used to remember that user has refused to download images in this session.") -(defvar emojify--download-in-progress-p nil - "Is emoji download in progress used to avoid multiple emoji download prompts.") - -(defun emojify--emoji-download-emoji-set (data) - "Download the emoji images according to DATA." - (let ((destination (expand-file-name (make-temp-name "emojify") - temporary-file-directory))) - (url-copy-file (ht-get data "url") - destination) - (let ((downloaded-sha (with-temp-buffer - (insert-file-contents-literally destination) - (secure-hash 'sha256 (current-buffer))))) - (when (string= downloaded-sha (ht-get data "sha256")) - destination)))) - -(defun emojify--extract-emojis (file) - "Extract the tar FILE in emoji directory." - (let* ((default-directory emojify-emojis-dir)) - (with-temp-buffer - (insert-file-contents-literally file) - (let ((emojify-inhibit-emojify-in-current-buffer-p t)) - (tar-mode)) - (tar-untar-buffer)))) - -(defun emojify-download-emoji (emoji-set) - "Download the provided EMOJI-SET." - (interactive (list (completing-read "Select the emoji set you want to download: " - (ht-keys emojify-emoji-set-json)))) - (let ((emoji-data (ht-get emojify-emoji-set-json emoji-set))) - (cond ((not emoji-data) - (error "No emoji set named %s found" emoji-set)) - ((and (file-exists-p (expand-file-name emoji-set emojify-emojis-dir)) - (called-interactively-p 'any)) - (message "%s emoji-set already downloaded, not downloading again!" emoji-set)) - (t - (emojify--extract-emojis (emojify--emoji-download-emoji-set (ht-get emojify-emoji-set-json emoji-set))))))) - -(defun emojify--confirm-emoji-download () - "Confirm download of emojis. - -This takes care of respecting the `emojify-download-emojis-p' and making sure we -do not prompt the user to download emojis multiple times." - (if (not (equal emojify-download-emojis-p 'ask)) - emojify-download-emojis-p - ;; Skip the prompt if we are in noninteractive mode or the user has already - ;; denied us permission to download once - (unless (or noninteractive emojify--refused-image-download-p) - (let ((download-confirmed-p (yes-or-no-p "[emojify] Emoji images not available should I download them now?"))) - (setq emojify--refused-image-download-p (not download-confirmed-p)) - download-confirmed-p)))) - -(defun emojify-download-emoji-maybe () - "Download emoji images if needed." - (when (and (equal emojify-display-style 'image) - (not (file-exists-p (emojify-image-dir))) - (not emojify--refused-image-download-p)) - (unwind-protect - ;; Do not prompt for download if download is in progress - (unless emojify--download-in-progress-p - (setq emojify--download-in-progress-p t) - (if (emojify--confirm-emoji-download) - (emojify-download-emoji emojify-emoji-set) - (warn "[emojify] Not downloading emoji images for now. Emojis would -not be displayed since images are not available. If you wish to download emojis, -run the command `emojify-download-emoji'"))) - (setq emojify--download-in-progress-p nil)))) - -(defun emojify-ensure-images () - "Ensure that emoji images are downloaded." - (if after-init-time - (emojify-download-emoji-maybe) - (add-hook 'after-init-hook #'emojify-download-emoji-maybe t))) - - - -;; Minor mode definitions - -(defun emojify-turn-on-emojify-mode () - "Turn on `emojify-mode' in current buffer." - - ;; Calculate emoji data if needed - (emojify-create-emojify-emojis) - - (when (emojify-buffer-p (current-buffer)) - ;; Download images if not available - (emojify-ensure-images) - - ;; Install our jit-lock function - (jit-lock-register #'emojify-redisplay-emojis-in-region) - (add-hook 'jit-lock-after-change-extend-region-functions #'emojify-after-change-extend-region-function t t) - - ;; Handle point entered behaviour - (add-hook 'post-command-hook #'emojify-detect-emoji-entry/exit t t) - - ;; Update emoji backgrounds after each command - (add-hook 'post-command-hook #'emojify-update-visible-emojis-background-after-command t t) - - ;; Update emoji backgrounds after mark is deactivated, this is needed since - ;; deactivation can happen outside the command loop - (add-hook 'deactivate-mark-hook #'emojify-update-visible-emojis-background-after-command t t) - - ;; Update emoji backgrounds after when window scrolls - (add-hook 'window-scroll-functions #'emojify-update-visible-emojis-background-after-window-scroll t t) - - ;; Redisplay emojis after enabling `prettify-symbol-mode' - (add-hook 'prettify-symbols-mode-hook #'emojify-redisplay-emojis-in-region) - - ;; Redisplay visible emojis when emoji style changes - (add-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region))) - -(defun emojify-turn-off-emojify-mode () - "Turn off `emojify-mode' in current buffer." - ;; Remove currently displayed emojis - (save-restriction - (widen) - (emojify-undisplay-emojis-in-region (point-min) (point-max))) - - ;; Uninstall our jit-lock function - (jit-lock-unregister #'emojify-redisplay-emojis-in-region) - (remove-hook 'jit-lock-after-change-extend-region-functions #'emojify-after-change-extend-region-function t) - - (remove-hook 'post-command-hook #'emojify-detect-emoji-entry/exit t) - - ;; Disable hooks to update emoji backgrounds - (remove-hook 'post-command-hook #'emojify-update-visible-emojis-background-after-command t) - (remove-hook 'deactivate-mark-hook #'emojify-update-visible-emojis-background-after-command t) - (remove-hook 'window-scroll-functions #'emojify-update-visible-emojis-background-after-window-scroll t) - - ;; Remove hook to redisplay emojis after enabling `prettify-symbol-mode' - (remove-hook 'prettify-symbols-mode-hook #'emojify-redisplay-emojis-in-region) - - ;; Remove style change hooks - (remove-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region)) - -;;;###autoload -(define-minor-mode emojify-mode - "Emojify mode" - :init-value nil - (if emojify-mode - ;; Turn on - (emojify-turn-on-emojify-mode) - ;; Turn off - (emojify-turn-off-emojify-mode))) - -;;;###autoload -(define-globalized-minor-mode global-emojify-mode - emojify-mode emojify-mode - :init-value nil) - -(defadvice set-buffer-multibyte (after emojify-disable-for-unibyte-buffers (&rest ignored)) - "Disable emojify when unibyte encoding is enabled for a buffer. -Re-enable it when buffer changes back to multibyte encoding." - (ignore-errors - (if enable-multibyte-characters - (when global-emojify-mode - (emojify-mode +1)) - (emojify-mode -1)))) - -(ad-activate #'set-buffer-multibyte) - - - -;; Displaying emojis in mode-line - -(defun emojify--emojied-mode-line (format) - "Return an emojified version of mode-line FORMAT. - -The format is converted to the actual string to be displayed using -`format-mode-line' and the unicode characters are replaced by images." - (if emojify-mode - ;; Remove "%e" from format since we keep it as first part of the - ;; emojified mode-line, see `emojify-emojify-mode-line' - (emojify-string (format-mode-line (delq "%e" format)) nil 'mode-line) - (format-mode-line format))) - -(defun emojify-mode-line-emojified-p () - "Check if the mode-line is already emojified. - -If the `mode-line-format' is of following format - -\(\"%e\" (:eval (emojify-emojied-mode-line ... ))) - -We can assume the mode-line is already emojified." - (and (consp mode-line-format) - (equal (ignore-errors (cl-caadr mode-line-format)) - :eval) - (equal (ignore-errors (car (cl-cadadr mode-line-format))) - 'emojify--emojied-mode-line))) - -(defun emojify-emojify-mode-line () - "Emojify unicode characters in the mode-line. - -This updates `mode-line-format' to a modified version which emojifies the -mode-line before it is displayed." - (unless (emojify-mode-line-emojified-p) - (setq mode-line-format `("%e" (:eval - (emojify--emojied-mode-line ',mode-line-format)))))) - -(defun emojify-unemojify-mode-line () - "Restore `mode-line-format' to unemojified version. - -This basically reverses the effect of `emojify-emojify-mode-line'." - (when (emojify-mode-line-emojified-p) - (setq mode-line-format (cl-cadadr (cl-cadadr mode-line-format))))) - -;;;###autoload -(define-minor-mode emojify-mode-line-mode - "Emojify mode line" - :init-value nil - (if emojify-mode-line-mode - ;; Turn on - (emojify-emojify-mode-line) - ;; Turn off - (emojify-unemojify-mode-line))) - -;;;###autoload -(define-globalized-minor-mode global-emojify-mode-line-mode - emojify-mode-line-mode emojify-mode-line-mode - :init-value nil) - - - -;; Searching emojis - -(defvar emojify-apropos-buffer-name "*Apropos Emojis*") - -(defun emojify-apropos-quit () - "Delete the window displaying Emoji search results." - (interactive) - (if (= (length (window-list)) 1) - (bury-buffer) - (quit-window))) - -(defun emojify-apropos-copy-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (goto-char (line-beginning-position)) - (if (not (get-text-property (point) 'emojified)) - (emojify-user-error "No emoji at point") - (kill-new (get-text-property (point) 'emojify-text)) - (message "Copied emoji (%s) to kill ring!" - (get-text-property (point) 'emojify-text))))) - -(defun emojify-apropos-describe-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (goto-char (line-beginning-position)) - (if (not (get-text-property (point) 'emojified)) - (emojify-user-error "No emoji at point") - (emojify-describe-emoji (get-text-property (point) 'emojify-text))))) - -(defvar emojify-apropos-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map emojify-common-mode-map) - (define-key map "c" #'emojify-apropos-copy-emoji) - (define-key map "w" #'emojify-apropos-copy-emoji) - (define-key map "d" #'emojify-apropos-describe-emoji) - (define-key map (kbd "RET") #'emojify-apropos-describe-emoji) - (define-key map "g" #'emojify-apropos-emoji) - map) - "Keymap used in `emojify-apropos-mode'.") - -(define-derived-mode emojify-apropos-mode fundamental-mode "Apropos Emojis" - "Mode used to display results of `emojify-apropos-emoji' - -\\{emojify-apropos-mode-map}" - (emojify-mode +1) - ;; view mode being a minor mode eats up our bindings avoid it - (let (view-read-only) - (read-only-mode +1))) - -(put 'emojify-apropos-mode 'mode-class 'special) - -(defvar emojify--apropos-last-query nil) -(make-variable-buffer-local 'emojify--apropos-last-query) - -(defun emojify-apropos-read-pattern () - "Read apropos pattern with INITIAL-INPUT as the initial input. - -Borrowed from apropos.el" - (let ((pattern (read-string (concat "Search for emoji (word list or regexp): ") - emojify--apropos-last-query))) - (if (string-equal (regexp-quote pattern) pattern) - (or (split-string pattern "[ \t]+" t) - (emojify-user-error "No word list given")) - pattern))) - -;;;###autoload -(defun emojify-apropos-emoji (pattern) - "Show Emojis that match PATTERN." - (interactive (list (emojify-apropos-read-pattern))) - - (emojify-create-emojify-emojis) - - (let ((in-apropos-buffer-p (equal major-mode 'emojify-apropos-mode)) - matching-emojis - sorted-emojis) - - (unless (listp pattern) - (setq pattern (list pattern))) - - ;; Convert the user entered text to a regex to match the emoji name or - ;; description - (apropos-parse-pattern pattern) - - ;; Collect matching emojis in a list of (list score emoji emoji-data) - ;; elements, where score is the proximity of the emoji to given pattern - ;; calculated using `apropos-score-str' - (emojify-emojis-each (lambda (key value) - (when (or (string-match apropos-regexp key) - (string-match apropos-regexp (ht-get value "name"))) - (push (list (max (apropos-score-str key) - (apropos-score-str (ht-get value "name"))) - key - value) - matching-emojis)))) - - ;; Sort the emojis by the proximity score - (setq sorted-emojis (mapcar #'cdr - (sort matching-emojis - (lambda (emoji1 emoji2) - (> (car emoji1) (car emoji2)))))) - - ;; Insert result in apropos buffer and display it - (with-current-buffer (get-buffer-create emojify-apropos-buffer-name) - (let ((inhibit-read-only t) - (query (mapconcat 'identity pattern " "))) - (erase-buffer) - (insert (propertize "Emojis matching" 'face 'apropos-symbol)) - (insert (format " - \"%s\"" query)) - (insert "\n\nUse `c' or `w' to copy emoji on current line\nUse `g' to rerun apropos\n\n") - (dolist (emoji sorted-emojis) - (insert (format "%s - %s (%s)" - (car emoji) - (ht-get (cadr emoji) "name") - (ht-get (cadr emoji) "style"))) - (insert "\n")) - (goto-char (point-min)) - (forward-line (1- 6)) - (emojify-apropos-mode) - (setq emojify--apropos-last-query (concat query " ")) - (setq-local line-spacing 7))) - - (pop-to-buffer (get-buffer emojify-apropos-buffer-name) - (when in-apropos-buffer-p - (cons #'display-buffer-same-window nil))))) - - - -;; Inserting emojis - -(defun emojify--completing-read-minibuffer-setup-hook () - "Enables `emojify-mode' in minbuffer while inserting emojis. - -This ensures `emojify' is enabled even when `global-emojify-mode' is not on." - (emojify-mode +1)) - -(defun emojify--completing-read-helm-hook () - "Enables `emojify-mode' in helm buffer. - -This ensures `emojify' is enabled in helm buffer displaying completion even when -`global-emojify-mode' is not on." - (with-current-buffer helm-buffer - (emojify-mode +1))) - -(defun emojify-completing-read (prompt &optional predicate require-match initial-input hist def inherit-input-method) - "Read emoji from the user and return the selected emoji. - -PROMPT is a string to prompt with, PREDICATE, REQUIRE-MATCH, INITIAL-INPUT, -HIST, DEF, INHERIT-INPUT-METHOD correspond to the arguments for -`completing-read' and are passed to ‘completing-read’ without any -interpretation. - -For each possible emoji PREDICATE is called with emoji text and data about the -emoji as a hash-table, the predate should return nil if it the emoji should -not be displayed for selection. - -For example the following can be used to display only github style emojis for -selection - -\(emojify-completing-read \"Select a Github style emoji: \" - (lambda (emoji data) - (equal (gethash \"style\" data) \"github\"))) - -This function sets up `ido', `icicles', `helm', `ivy' and vanilla Emacs -completion UI to display properly emojis." - (emojify-create-emojify-emojis) - (let* ((emojify-minibuffer-reading-emojis-p t) - (line-spacing 7) - (completion-ignore-case t) - (candidates (emojify--get-completing-read-candidates)) - ;; Vanilla Emacs completion and Icicles use the completion list mode to display candidates - ;; the following makes sure emojify is enabled in the completion list - (completion-list-mode-hook (cons #'emojify--completing-read-minibuffer-setup-hook - completion-list-mode-hook)) - ;; (Vertical) Ido and Ivy displays candidates in minibuffer this makes sure candidates are emojified - ;; when Ido or Ivy are used - (minibuffer-setup-hook (cons #'emojify--completing-read-minibuffer-setup-hook - minibuffer-setup-hook)) - (helm-after-initialize-hook (cons #'emojify--completing-read-helm-hook - (bound-and-true-p helm-after-initialize-hook)))) - (car (split-string (completing-read prompt - candidates - predicate - require-match - initial-input - hist - def - inherit-input-method) - " ")))) - -;;;###autoload -(defun emojify-insert-emoji () - "Interactively prompt for Emojis and insert them in the current buffer. - -This respects the `emojify-emoji-styles' variable." - (interactive) - (insert (emojify-completing-read "Insert Emoji: "))) - - - -;; Describing emojis - -(defvar emojify-help-buffer-name "*Emoji Help*") - -(defvar-local emojify-described-emoji nil) - -(defun emojify-description-copy-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (kill-new emojify-described-emoji) - (message "Copied emoji (%s) to kill ring!" emojify-described-emoji))) - -(defvar emojify-description-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map emojify-common-mode-map) - (define-key map "c" #'emojify-description-copy-emoji) - (define-key map "w" #'emojify-description-copy-emoji) - map) - "Keymap used in `emojify-description-mode'.") - -(define-derived-mode emojify-description-mode fundamental-mode "Describe Emoji" - "Mode used to display results of description for emojis. - -\\{emojify-description-mode-map}" - (emojify-mode +1) - ;; view mode being a minor mode eats up our bindings avoid it - (let (view-read-only) - (read-only-mode +1)) - (goto-address-mode +1)) - -(put 'emojify-description-mode 'mode-class 'special) - -(defun emojify--display-emoji-description-buffer (emoji) - "Display description for EMOJI." - (with-current-buffer (get-buffer-create emojify-help-buffer-name) - (let ((inhibit-read-only t)) - (erase-buffer) - (save-excursion - (insert (propertize (ht-get emoji "emoji") 'emojify-inhibit t) - " - Displayed as " - (propertize (ht-get emoji "emoji") 'emojify-force-display t) - "\n\n") - (insert (propertize "Name" 'face 'font-lock-keyword-face) - ": " - (ht-get emoji "name") "\n") - (insert (propertize "Style" 'face 'font-lock-keyword-face) - ": " - (ht-get emoji "style") "\n") - (insert (propertize "Image used" 'face 'font-lock-keyword-face) - ": " - (expand-file-name (ht-get emoji "image") - (emojify-image-dir)) - "\n") - (when (and (not (string= (ht-get emoji "style") "unicode")) - (ht-get emoji "unicode")) - (insert (propertize "Unicode representation" - 'face 'font-lock-keyword-face) - ": " - (propertize (ht-get emoji "unicode") 'emojify-inhibit t) - "\n")) - (when (and (not (string= (ht-get emoji "style") "ascii")) - (ht-get emoji "ascii")) - (insert (propertize "Ascii representation" - 'face 'font-lock-keyword-face) - ": " - (propertize (ht-get emoji "ascii") 'emojify-inhibit t) - "\n")) - (insert (propertize "User defined" - 'face 'font-lock-keyword-face) - ": " - (if (ht-get emoji "custom") "Yes" "No") - "\n") - (unless (ht-get emoji "custom") - (when (or (ht-get emoji "unicode") - (string= (ht-get emoji "style") "unicode")) - (insert (propertize "Unicode Consortium" 'face 'font-lock-keyword-face) - ": " - (concat "http://www.unicode.org/emoji/charts-beta/full-emoji-list.html#" - (string-join (mapcar (apply-partially #'format "%x") - (string-to-list (or (ht-get emoji "unicode") - (ht-get emoji "emoji")))) - "_")) - "\n")) - (insert (propertize "Emojipedia" 'face 'font-lock-keyword-face) - ": " - (let* ((tone-stripped (replace-regexp-in-string "- *[Tt]one *\\([0-9]+\\)$" - "- type \\1" - (ht-get emoji "name"))) - (non-alphanumeric-stripped (replace-regexp-in-string "[^0-9a-zA-Z]" - " " - tone-stripped)) - (words (split-string non-alphanumeric-stripped " " t " "))) - (concat "http://emojipedia.org/" - (downcase (emojify--string-join words "-")))) - "\n")))) - (emojify-description-mode) - (setq emojify-described-emoji (ht-get emoji "emoji"))) - (display-buffer (get-buffer emojify-help-buffer-name)) - (get-buffer emojify-help-buffer-name)) - -(defun emojify-describe-emoji (emoji-text) - "Display description for EMOJI-TEXT." - (interactive (list (emojify-completing-read "Describe Emoji: "))) - (if (emojify-get-emoji emoji-text) - (emojify--display-emoji-description-buffer (emojify-get-emoji emoji-text)) - (emojify-user-error "No emoji found for '%s'" emoji-text))) - -(defun emojify-describe-emoji-at-point () - "Display help for EMOJI displayed at point." - (interactive) - (if (not (get-text-property (point) 'emojified)) - (emojify-user-error "No emoji at point!") - (emojify--display-emoji-description-buffer (emojify-get-emoji (get-text-property (point) 'emojify-text))))) - - - -;; Listing emojis - -(defun emojify-list-copy-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (let ((emoji (get-text-property (point) 'tabulated-list-id))) - (if (not emoji) - (emojify-user-error "No emoji at point") - (kill-new emoji) - (message "Copied emoji (%s) to kill ring!" emoji))))) - -(defun emojify-list-describe-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (let ((emoji (get-text-property (point) 'tabulated-list-id))) - (if (not emoji) - (emojify-user-error "No emoji at point") - (emojify-describe-emoji emoji))))) - -(defvar-local emojify-list--emojis-displayed nil - "Record that emojis have been successfully displayed in the current buffer. - -`emojify-list-emojis' checks to this decide if it should print the entries -again.") - -(defun emojify-list-force-refresh () - "Force emoji list to be refreshed." - (interactive) - (setq emojify-list--emojis-displayed nil) - (emojify-list-emojis)) - -(defvar emojify-list-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map emojify-common-mode-map) - (define-key map "c" #'emojify-list-copy-emoji) - (define-key map "w" #'emojify-list-copy-emoji) - (define-key map "d" #'emojify-list-describe-emoji) - (define-key map "g" #'emojify-list-force-refresh) - (define-key map (kbd "RET") #'emojify-list-describe-emoji) - map) - "Keymap used in `emojify-list-mode'.") - -(defun emojify-list-printer (id cols) - "Printer used to print the emoji rows in tabulated list. - -See `tabulated-list-print-entry' to understand the arguments ID and COLS." - (let ((beg (point)) - (padding (max tabulated-list-padding 0)) - (inhibit-read-only t)) - (when (> tabulated-list-padding 0) - (insert (make-string padding ?\s))) - - (tabulated-list-print-col 0 - (propertize (aref cols 0) 'emojify-inhibit t) - (current-column)) - - ;; Inhibit display of second column ("Text") as emoji - (tabulated-list-print-col 1 - (propertize (aref cols 1) 'emojify-inhibit t) - (current-column)) - - ;; The type of this emoji - (tabulated-list-print-col 2 - (aref cols 2) - (current-column)) - - ;; Is this a custom emoji - (tabulated-list-print-col 3 - (aref cols 3) - (current-column)) - - ;; Force display of last column ("Display") as emoji - (tabulated-list-print-col 4 - (propertize (aref cols 4) 'emojify-force-display t) - (current-column)) - - (insert ?\n) - (add-text-properties beg - (point) - `(tabulated-list-id ,id tabulated-list-entry ,cols)) - - (message "Listing emojis (%d of %d) ..." (1- (line-number-at-pos)) (aref cols 5)))) - -(defun emojify-list-entries () - "Return entries to display in tabulated list." - (emojify-create-emojify-emojis) - - (let (entries count) - (emojify-emojis-each (lambda (emoji data) - (push (list emoji (vector (ht-get data "name") - emoji - (ht-get data "style") - (if (ht-get data "custom") "Yes" "No") - emoji)) - entries))) - - (setq count (length entries)) - - (mapcar (lambda (entry) - (list (car entry) (vconcat (cadr entry) (vector count)))) - entries))) - -(define-derived-mode emojify-list-mode tabulated-list-mode "Emoji-List" - "Major mode for listing emojis. -\\{emojify-list-mode-map}" - (setq line-spacing 7 - tabulated-list-format [("Name" 30 t) - ("Text" 20 t) - ("Style" 10 t) - ("Custom" 10 t) - ("Display" 20 nil)] - tabulated-list-sort-key (cons "Name" nil) - tabulated-list-padding 2 - tabulated-list-entries #'emojify-list-entries - tabulated-list-printer #'emojify-list-printer) - (tabulated-list-init-header)) - -(defun emojify-list-emojis () - "List emojis in a tabulated view." - (interactive) - (let ((buffer (get-buffer-create "*Emojis*"))) - (with-current-buffer buffer - (unless emojify-list--emojis-displayed - (emojify-list-mode) - (tabulated-list-print) - (setq emojify-list--emojis-displayed t)) - (pop-to-buffer buffer)))) - - - -;; Integration with company mode - -(defadvice company-pseudo-tooltip-unhide (after emojify-display-emojis-in-company-tooltip (&rest ignored)) - "Advice to display emojis in company mode tooltips. - -This function does two things, first it adds text properties to the completion -tooltip to make it display the emojis, secondly it makes company always render -the completion tooltip using `after-string' overlay property rather than -`display' text property. - -The second step is needed because emojify displays the emojis using `display' -text property, similarly company-mode in some cases uses `display' overlay -property to render its pop, this results into a `display' property inside -`display' property, however Emacs ignores recursive display text property. -Using `after-string' works as well as `display' while allowing the emojis to be -displayed." - (when (and emojify-mode - emojify-company-tooltips-p - (overlayp (bound-and-true-p company-pseudo-tooltip-overlay))) - (let* ((ov company-pseudo-tooltip-overlay) - (disp (or (overlay-get ov 'display) - (overlay-get ov 'after-string))) - (emojified-display (when disp - (emojify-string disp))) - (emojified-p (when emojified-display - (text-property-any 0 (1- (length emojified-display)) - 'emojified t - emojified-display)))) - ;; Do not switch to after-string if menu is not emojified - (when (and disp emojified-p) - (overlay-put ov 'after-string nil) - (overlay-put ov 'display (propertize " " 'invisible t)) - (overlay-put ov 'after-string emojified-display))))) - -(ad-activate #'company-pseudo-tooltip-unhide) - - - -;; Integration with some miscellaneous functionality - -(defadvice mouse--drag-set-mark-and-point (after emojify-update-emoji-background (&rest ignored)) - "Advice to update emoji backgrounds after selection is changed using mouse. - -Currently there are no hooks run after mouse movements, as such the emoji -backgrounds are updated only after the mouse button is released. This advices -`mouse--drag-set-mark-and-point' which is run after selection changes to trigger -an update of emoji backgrounds. Not the cleanest but the only way I can think of." - (when emojify-mode - (emojify-update-visible-emojis-background-after-command))) - -(ad-activate #'mouse--drag-set-mark-and-point) - -(defadvice text-scale-increase (after emojify-resize-emojis (&rest ignored)) - "Advice `text-scale-increase' to resize emojis on text resize." - (when emojify-mode - (let ((new-font-height (emojify-default-font-height))) - (emojify-do-for-emojis-in-region (point-min) (point-max) - (plist-put (cdr (get-text-property emoji-start 'display)) - :height - new-font-height))))) - -(ad-activate #'text-scale-increase) - - - -(provide 'emojify) -;;; emojify.el ends here |