commit eacd56bd6a275f7188faf29f1de8ec4c509204bd
parent f791ad4c741218668dad4ba8db65161da9a5b010
Author: Iqbal Ansari <iqbalansari02@yahoo.com>
Date: Wed, 7 Sep 2016 23:58:01 +0530
Merge branch 'develop'
Diffstat:
7 files changed, 578 insertions(+), 154 deletions(-)
diff --git a/README.org b/README.org
@@ -20,6 +20,8 @@
- [[#configuring-the-texts-that-are-displayed-as-emojis][Configuring the texts that are displayed as emojis]]
- [[#controlling-behaviour-when-point-enters-an-emoji][Controlling behaviour when point enters an emoji]]
- [[#controlling-behaviour-when-mouse-hovers-over-an-emoji][Controlling behaviour when mouse hovers over an emoji]]
+ - [[#custom-emojis][Custom emojis]]
+ - [[#integration-with-prettify-symbol-mode][Integration with prettify-symbol-mode]]
- [[#known-issues][Known issues]]
- [[#contributing][Contributing]]
- [[#thanks][Thanks]]
@@ -78,16 +80,22 @@
(add-hook 'after-init-hook #'global-emojify-mode)
#+END_SRC
- In programming modes only emojis in string and comments are displayed.
+ Additionally ~emojify~ can integrate with ~prettify-symbols-mode~ displaying
+ the symbols it recognizes as emojis. This is disabled by default, see
+ [[#integration-with-prettify-symbol-mode][Integration with prettify-symbol-mode]].
+
+ In programming modes ~github~ and ~ascii~ style emojis are displayed only in
+ string and comments, ~unicode~ emojis are displayed in all contexts. The
+ emojis of type ~prettify-symbol~ are displayed only in code context.
*** Searching emojis
The command ~emojify-apropos-emoji~ can be used to display emojis that match
given regexp/apropos pattern. The results are displayed in a specialized
buffer, where ~w~ or ~c~ can be used to copy emojis to the kill ring.
- *NOTE* - You might notice that some of the emojis returned by this search
- are not displayed by ~emojify~, this might happen if you newer emoji data
- but old set of image. Download the latest emoji image using
+ *NOTE* - You might notice that some of the emojis prompted in the above
+ command are not displayed by ~emojify~, this might happen if you have newer
+ emoji data but old set of images. Download the latest emoji image using
~emojify-download-emoji~ and set ~emojify-emoji-set~ to the downloaded set.
*** Inserting emojis
@@ -97,8 +105,8 @@
depending on you preference.
*NOTE* - You might notice that some of the emojis prompted in the above
- command are not displayed by ~emojify~, this might happen if you newer emoji
- data but old set of image. Download the latest emoji image using
+ command are not displayed by ~emojify~, this might happen if you have newer
+ emoji data but old set of images. Download the latest emoji image using
~emojify-download-emoji~ and set ~emojify-emoji-set~ to the downloaded set.
** Customizations
@@ -111,9 +119,11 @@
one parameter the list of styles that you want to be displayed. The possible
styles are
- - ascii - Display only plain ascii emojis
- - unicode - Display only unicode emojis
- - github - Display only github style emojis
+ - ascii - Display only plain ascii emojis
+ - unicode - Display only unicode emojis
+ - github - Display only github style emojis
+ - prettify-symbol - Display symbols from ~prettify-symbols-alist~
+ (which emojify recognizes) as emojis
*** Configuring how emojis are displayed
By default emojis are displayed using images. However you can instruct emojify
@@ -198,12 +208,62 @@
When mouse hovers over a emoji, the underlying text is displayed in a help
popup. This behaviour can be disabled by setting ~emojify-show-help~ to nil.
+*** Custom emojis
+ You can specify custom emojis using the ~emojify-user-emojis~ variable. You
+ need to set it to 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
+
+ 1) "name" - The name of the emoji
+ 2) "style" - This should be one of "github", "ascii" or "github"
+ Note: "prettify-symbol" is not a valid style for custom emojis
+
+ Additionally the alist should contain one of (see [[#configuring-the-types-of-emojis-displayed][emojify-display-style]])
+ 1) "unicode" - The replacement for the provided emoji for "unicode" display style
+ 2) "image" - The replacement for the provided emoji for "image" display style.
+ This should be the *absolute* path to the image
+ 3) "ascii" - The replacement for the provided emoji for "ascii" display style
+
+ It is best to set this variable before you load ~emojify~, in case you set
+ this variable after loading ~emojify~ run the function
+ ~emojify-set-emoji-data~ to recalculate emoji data.
+
+ User emojis take precedence over default emojis so the above mechanism can
+ also be used to override the default emojis
+
+**** Example
+ Below is an example of setting up custom emojis. Assuming that the custom
+ images are at ~\~/.emacs.d/emojis/trollface.png~ and
+ ~\~/.emacs.d/emojis/neckbeard.png~, you instruct ~emojify~ to display ~:trollface:~
+ and ~:neckbeard:~ as :trollface: and :neckbeard:
+
+ #+BEGIN_SRC emacs-lisp
+ (setq emojify-user-emojis '((":trollface:" . (("name" . "Troll Face")
+ ("image" . "~/.emacs.d/emojis/trollface.png")
+ ("style" . "github")))
+ (":neckbeard:" . (("name" . "Neckbeard")
+ ("image" . "~/.emacs.d/emojis/neckbeard.png")
+ ("style" . "github")))))
+
+ ;; If emojify is already loaded refresh emoji data
+ (when (featurep 'emojify)
+ (emojify-set-emoji-data))
+ #+END_SRC
+
+** Integration with prettify-symbol-mode
+ Emojify can be configured to display symbols from ~prettify-symbols-alist~ as
+ emojis. To do so add ~prettify-symbol~ to [[#configuring-the-types-of-emojis-displayed][emojify-emoji-styles]]. You might
+ need re-enable ~prettify-symbols-mode~ if you are already using it, ~emojify~
+ will automatically display any symbols from ~prettify-symbols-alist~ that it
+ recognizes as emojis. You can use [[#custom-emojis][emojify-user-emojis]] to teach ~emojify~
+ about it any symbol it does not recognize yet.
+
** Known issues
- Emojis are not properly updated after customizing ~emojify-display-style~ or
- ~emojify-prog-contexts~. This would be fixed in future. For time being you
- will be fine as long as you set these variables before ~emojify~ has
- loaded.
- - There is currently no support for custom emojis/images. I plan to add this in future.
+ ~emojify-program-contexts~. For time being you will be fine as long as you
+ set these variables before ~emojify~ has loaded.
** Contributing
Code as well as documentation contributions are welcome. Development on
diff --git a/emojify.el b/emojify.el
@@ -331,30 +331,9 @@ can customize `emojify-inhibit-major-modes' and
;; Customizations to control display of emojis
-(defvar emojify-emojis nil
- "Data about the emojis, this contains only the emojis that come with emojify.")
-
-(defvar emojify-regexps nil
- "Regexp to match text to emojified.")
-
-(defvar emojify-emoji-style-change-hooks nil
+(defvar emojify-emoji-style-change-hook nil
"Hooks run when emoji style changes.")
-(defun emojify-set-emoji-data ()
- "Read the emoji data for STYLES and set the regexp required to search them."
- (setq emojify-emojis (let ((json-array-type 'list)
- (json-object-type 'hash-table))
- (json-read-file emojify-emoji-json)))
-
- ;; Construct emojify-regexps in descending order of length, this is important
- ;; so that larger emojis are searched first and get precedence over smaller
- ;; ones (see also `emojify-display-emojis-in-region')
- (setq emojify-regexps (seq-map #'regexp-opt
- (seq-partition (sort (ht-keys emojify-emojis)
- (lambda (string1 string2) (> (length string1)
- (length string2))))
- 1000))))
-
;;;###autoload
(defun emojify-set-emoji-styles (styles)
"Set the type of emojis that should be displayed.
@@ -366,7 +345,7 @@ STYLES is the styles emoji styles that should be used, see `emojify-emoji-styles
(setq-default emojify-emoji-styles styles)
- (run-hooks 'emojify-emoji-style-change-hooks))
+ (run-hooks 'emojify-emoji-style-change-hook))
(defcustom emojify-emoji-styles
'(ascii unicode github)
@@ -374,13 +353,15 @@ STYLES is the styles emoji styles that should be used, see `emojify-emoji-styles
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:'"
+`ascii' - Display only ascii emojis for example ';)'
+`unicode' - Display only unicode emojis for example '😉'
+`github' - Display only github style emojis for example ':wink:'
+`prettify-symbol' - Display only emojis extracted from `prettify-symbols-alist'"
:type '(set
(const :tag "Display only ascii emojis" ascii)
(const :tag "Display only github emojis" github)
- (const :tag "Display only unicode codepoints" unicode))
+ (const :tag "Display only unicode codepoints" unicode)
+ (const :tag "Display only emojis extracted from `prettify-symbols-alist'" prettify-symbol))
:set (lambda (_ value) (emojify-set-emoji-styles value))
:group 'emojify)
@@ -444,11 +425,15 @@ This returns non-nil if the region is valid according to `emojify-program-contex
(t 'code))))
(and (memql context emojify-program-contexts)
(if (equal context 'code)
- ;; If context if code display only unicode emojis
- (and (string= (ht-get emoji "style") "unicode")
- (memql 'unicode emojify-emoji-styles))
- ;; No need to check for non-code context
- t)))))
+ ;; If context is code display only unicode emojis
+ (or (and (string= (ht-get emoji "style") "unicode")
+ (memql 'unicode emojify-emoji-styles))
+ (and (string= (ht-get emoji "style") "prettify-symbol")
+ (bound-and-true-p prettify-symbols-mode)
+ (memql 'prettify-symbol emojify-emoji-styles)))
+ ;; Display any other style in all contexts except for
+ ;; prettify-symbol emoji
+ (not (string= (ht-get emoji "style") "prettify-symbol")))))))
(defun emojify-inside-org-src-p (point)
"Return non-nil if POINT is inside `org-mode' src block.
@@ -604,6 +589,119 @@ To understand WINDOW, STRING and POS see the function documentation for
;; Core functions and macros
+(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')
+ Note: \"prettify-symbol\" is not a valid style for custom emojis
+
+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-emojis nil
+ "Data about the emojis, this contains only the emojis that come with emojify.")
+
+(defvar emojify-pretty-symbol-emojis nil
+ "Emojis extracted from `prettify-symbols-alist'.")
+
+(defvar emojify--user-emojis nil
+ "User specified custom emojis.")
+
+(defvar emojify-regexps nil
+ "Regexp to match text to emojified.")
+
+(make-variable-buffer-local 'emojify-regexps)
+(make-variable-buffer-local 'emojify-pretty-symbol-emojis)
+
+(defun emojify-create-emojify-emojis ()
+ "Create `emojify-emojis' if needed."
+ (unless 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',
+`emojify-pretty-symbol-emojis' and finally in `emojify-emojis'."
+ (or (when emojify--user-emojis
+ (ht-get emojify--user-emojis emoji))
+ (when emojify-pretty-symbol-emojis
+ (ht-get emojify-pretty-symbol-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',
+`emojify-pretty-symbol-emojis' and then `emojify-emojis'."
+ (when emojify--user-emojis
+ (ht-each function emojify--user-emojis))
+ (when emojify-pretty-symbol-emojis
+ (ht-each function emojify-pretty-symbol-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)))
+
+ ;; Construct emojify-regexps in descending order of length, this is important
+ ;; so that larger emojis are searched first and get precedence over smaller
+ ;; ones (see also `emojify-display-emojis-in-region')
+ (setq-default emojify-regexps (seq-map #'regexp-opt
+ (seq-partition (sort (ht-keys emojify-emojis)
+ (lambda (string1 string2) (> (length string1)
+ (length string2))))
+ 1000)))
+ (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-default emojify--user-emojis (ht-from-alist emoji-pairs))
+ (setq-default emojify-regexps (cons (regexp-opt (mapcar #'car emoji-pairs))
+ emojify-regexps)))
+ (message "[emojify] User emojis are not in correct format ignoring them."))))
+
(defvar emojify-emoji-keymap
(let ((map (make-sparse-keymap)))
(define-key map [remap delete-char] #'emojify-delete-emoji-forward)
@@ -744,22 +842,23 @@ selection, but for some reason it does not work well."
DATA holds the emoji data, BEG and END delimit the region where emoji will
be displayed."
- (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 (and (fboundp 'imagemagick-types)
- (memq image-type (imagemagick-types)))
- 'imagemagick)
- nil
- :ascent 'center
- :heuristic-mask t
- :background (emojify--get-image-background beg end)
- ;; no-op if imagemagick is not available
- :height (emojify-default-font-height)))))
+ (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 (and (fboundp 'imagemagick-types)
+ (memq image-type (imagemagick-types)))
+ 'imagemagick)
+ nil
+ :ascent 'center
+ :heuristic-mask t
+ :background (emojify--get-image-background beg end)
+ ;; no-op if imagemagick is not available
+ :height (emojify-default-font-height))))))
(defun emojify--get-unicode-display (data _beg _end)
"Get the display text property to display the emoji as an unicode character.
@@ -807,7 +906,7 @@ TODO: Skip emojifying if region is already emojified."
(match-end (match-end 0))
(match (match-string-no-properties 0))
(buffer (current-buffer))
- (emoji (ht-get emojify-emojis match)))
+ (emoji (emojify-get-emoji match)))
(when (and (memql (intern (ht-get emoji "style"))
emojify-emoji-styles)
;; Skip displaying this emoji if the its bounds are
@@ -901,10 +1000,12 @@ BEG and END are the beginning and end of the region respectively"
Redisplay emojis in the visible region if BEG and END are not specified"
(let* ((area (emojify--get-relevant-region))
(beg (or beg (car area)))
- (end (or end (cdr area))))
- (emojify-execute-ignoring-errors-unless-debug
- (emojify-undisplay-emojis-in-region beg end)
- (emojify-display-emojis-in-region beg end))))
+ (end (or end (cdr area)))
+ (region-too-big (> end (+ beg (* 10 (- (window-end) (window-start)))))))
+ (unless region-too-big
+ (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.
@@ -1030,6 +1131,41 @@ of the window. DISPLAY-START corresponds to the new start of the window."
+;; Integration with prettify-symbols-mode
+
+(defun emojify-populate-emojis-from-prettify-symbol-mode ()
+ "Populate additional text to display from `prettify-symbols-alist'."
+ (when (and (seq-position emojify-emoji-styles 'prettify-symbol)
+ (bound-and-true-p prettify-symbols-alist))
+
+ (let (new-regexps emojis)
+ (dolist (pretty-symbol prettify-symbols-alist)
+ (let* ((symbol-text (make-string 1 (cdr pretty-symbol)))
+ (emojify-symbol-data (emojify-get-emoji symbol-text)))
+ (when emojify-symbol-data
+ (push (cons (car pretty-symbol)
+ (ht-from-alist (list (cons "style" "prettify-symbol")
+ (cons "image" (gethash "image" emojify-symbol-data))
+ (cons "unicode" symbol-text)
+ (cons "name" (format "Pretty represenation for '%s'" (car pretty-symbol))))))
+ emojis)
+ (push (car pretty-symbol) new-regexps))))
+
+ (when emojis
+ (setq emojify-pretty-symbol-emojis (ht-from-alist emojis)))
+
+ (when new-regexps
+ (let ((re (regexp-opt new-regexps 'symbols)))
+ (setq emojify-regexps (cons re (delete re emojify-regexps))))))))
+
+(defun emojify-handle-prettify-symbol-mode ()
+ "Redisplay emojis after `prettify-symbol-mode' is enabled/disabled."
+ (when (bound-and-true-p prettify-symbols-mode)
+ (emojify-populate-emojis-from-prettify-symbol-mode))
+ (emojify-redisplay-emojis-in-region))
+
+
+
;; Lazy image downloading
(defvar emojify--refused-image-download-p nil
@@ -1101,8 +1237,7 @@ run the command `emojify-download-emoji'")))
"Turn on `emojify-mode' in current buffer."
;; Calculate emoji data if needed
- (unless emojify-emojis
- (emojify-set-emoji-data))
+ (emojify-create-emojify-emojis)
(when (emojify-buffer-p (current-buffer))
;; Download images if not available
@@ -1123,7 +1258,14 @@ run the command `emojify-download-emoji'")))
(add-hook 'window-scroll-functions #'emojify-update-visible-emojis-background-after-window-scroll t t)
;; Redisplay visible emojis when emoji style changes
- (add-hook 'emojify-emoji-style-change-hooks #'emojify-redisplay-emojis-in-region)))
+ (add-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region)
+
+ ;; Add symbols from prettify symbol mode, to displayed emojis and redisplay
+ ;; emojis when prettify-symbols-mode is activated
+ (add-hook 'prettify-symbols-mode-hook #'emojify-handle-prettify-symbol-mode)
+
+ ;; Repopulate emojis from prettify-symbols-alist when style changes
+ (add-hook 'emojify-emoji-style-change-hook #'emojify-handle-prettify-symbol-mode)))
(defun emojify-turn-off-emojify-mode ()
"Turn off `emojify-mode' in current buffer."
@@ -1141,8 +1283,12 @@ run the command `emojify-download-emoji'")))
(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)
+ ;; Disable display of symbols
+ (remove-hook 'prettify-symbols-mode-hook #'emojify-handle-prettify-symbol-mode)
+
;; Remove style change hooks
- (remove-hook 'emojify-emoji-style-change-hooks #'emojify-redisplay-emojis-in-region))
+ (remove-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region)
+ (remove-hook 'emojify-emoji-style-change-hook #'emojify-handle-prettify-symbol-mode))
;;;###autoload
(define-minor-mode emojify-mode
@@ -1163,6 +1309,8 @@ run the command `emojify-download-emoji'")))
;; Searching and inserting emojis
+(defvar emojify-apropos-buffer-name "*Apropos Emojis*")
+
(defun emojify-apropos-quit ()
"Delete the window displaying Emoji search results."
(interactive)
@@ -1190,6 +1338,7 @@ run the command `emojify-download-emoji'")))
(define-key map "p" #'previous-line)
(define-key map "r" #'isearch-backward)
(define-key map "s" #'isearch-forward)
+ (define-key map "g" #'emojify-apropos-emoji)
(define-key map ">" 'end-of-buffer)
(define-key map "<" 'beginning-of-buffer)
@@ -1211,12 +1360,32 @@ run the command `emojify-download-emoji'")))
(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)
+ (if (fboundp 'user-error)
+ (apply #'user-error "No word list given")
+ (apply #'error "No word list given")))
+ pattern)))
+
;;;###autoload
(defun emojify-apropos-emoji (pattern)
"Show Emojis that match PATTERN."
- (interactive (list (apropos-read-pattern "emoji")))
+ (interactive (list (emojify-apropos-read-pattern)))
+
+ (emojify-create-emojify-emojis)
- (let (matching-emojis sorted-emojis)
+ (let ((in-apropos-buffer-p (equal major-mode 'emojify-apropos-mode))
+ matching-emojis
+ sorted-emojis)
(unless (listp pattern)
(setq pattern (list pattern)))
@@ -1225,18 +1394,17 @@ run the command `emojify-download-emoji'")))
;; description
(apropos-parse-pattern pattern)
- ;; Collection matching emojis in a list (list score emoji emoji-data)
+ ;; 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'
- (maphash (lambda (key value)
- (when (or (string-match apropos-regexp key)
- (string-match apropos-regexp (gethash "name" value)))
- (push (list (max (apropos-score-str key)
- (apropos-score-str (gethash "name" value)))
- key
- value)
- matching-emojis)))
- emojify-emojis)
+ (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
@@ -1244,26 +1412,29 @@ run the command `emojify-download-emoji'")))
(lambda (emoji1 emoji2)
(> (car emoji1) (car emoji2))))))
- (when (get-buffer "*Apropos Emojis*")
- (kill-buffer "*Apropos Emojis*"))
-
;; Insert result in apropos buffer and display it
- (with-current-buffer (get-buffer-create "*Apropos Emojis*")
- (erase-buffer)
- (insert (propertize "Emojis matching" 'face 'apropos-symbol))
- (insert (format " - \"%s\"" (mapconcat 'identity pattern " ")))
- (insert "\n\nUse `c' or `w' to copy emoji on current line\n\n")
- (dolist (emoji sorted-emojis)
- (insert (format "%s - %s (%s)"
- (car emoji)
- (gethash "name" (cadr emoji))
- (gethash "style" (cadr emoji))))
- (insert "\n"))
- (goto-char (point-min))
- (emojify-apropos-mode)
- (setq-local line-spacing 7))
-
- (display-buffer (get-buffer "*Apropos Emojis*"))))
+ (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)))
+
+ (select-window (display-buffer (get-buffer emojify-apropos-buffer-name)
+ (when in-apropos-buffer-p
+ (cons #'display-buffer-same-window nil))))))
;;;###autoload
(defun emojify-insert-emoji ()
@@ -1271,21 +1442,21 @@ run the command `emojify-download-emoji'")))
This respects the `emojify-emoji-styles' variable."
(interactive)
+ (emojify-create-emojify-emojis)
(let* ((emojify-in-insertion-command-p t)
(styles (mapcar #'symbol-name emojify-emoji-styles))
(line-spacing 7)
(completion-ignore-case t)
(candidates (let (emojis)
- (maphash (lambda (key value)
- (when (member (gethash "style" value) styles)
- (push (format "%s - %s (%s)"
- key
- (gethash "name" value)
- (gethash "style" value))
- emojis)))
- emojify-emojis)
+ (emojify-emojis-each (lambda (key value)
+ (when (seq-position styles (ht-get value "style"))
+ (push (format "%s - %s (%s)"
+ key
+ (ht-get value "name")
+ (ht-get value "style"))
+ emojis))))
emojis)))
- (insert (car (split-string (completing-read "Apropos Emoji: " candidates)
+ (insert (car (split-string (completing-read "Insert Emoji: " candidates)
" ")))))
diff --git a/test/assets/lambda.png b/test/assets/lambda.png
Binary files differ.
diff --git a/test/assets/neckbeard.png b/test/assets/neckbeard.png
Binary files differ.
diff --git a/test/assets/trollface.png b/test/assets/trollface.png
Binary files differ.
diff --git a/test/emojify-test.el b/test/emojify-test.el
@@ -63,45 +63,69 @@
(emojify-redisplay-emojis-in-region)
(emojify-tests-should-not-be-emojified (point-min))))
+(ert-deftest emojify-test-custom-emojis ()
+ :tags '(core custom-images)
+ (let ((emojify-user-emojis emojify-test-custom-emojis))
+ (emojify-set-emoji-data)
+ (emojify-tests-with-emojified-static-buffer ":neckbeard:
+:troll:"
+ (emojify-tests-should-be-emojified (point-min))
+ (should (equal (get-text-property (point) 'emojify-buffer) (current-buffer)))
+ (should (= (get-text-property (point-min) 'emojify-beginning) (point-min-marker)))
+ (should (= (get-text-property (point) 'emojify-end) (line-end-position 1)))
+ (should (equal (get-text-property (point) 'emojify-text) ":neckbeard:"))
+
+ (emojify-tests-should-be-emojified (line-beginning-position 2))
+ (should (equal (get-text-property (line-beginning-position 2) 'emojify-buffer) (current-buffer)))
+ (should (= (get-text-property (line-beginning-position 2) 'emojify-beginning) (line-beginning-position 2)))
+ (should (= (get-text-property (line-beginning-position 2) 'emojify-end) (point-max-marker)))
+ (should (equal (get-text-property (line-beginning-position 2) 'emojify-text) ":troll:")))))
+
(ert-deftest emojify-tests-mixed-emoji-test ()
:tags '(core mixed)
- (emojify-tests-with-emojified-static-buffer "😉\n:D\nD:\n:smile:"
- (emojify-tests-should-be-emojified (point-min))
- (emojify-tests-should-be-emojified (line-beginning-position 2))
- (emojify-tests-should-be-emojified (line-beginning-position 3))
- (emojify-tests-should-be-emojified (line-beginning-position 4))))
-
-;; The after-change tests stopped working after moving to JIT lock :unamused:
-;; (ert-deftest emojify-tests-emojifying-on-comment-uncomment ()
-;; :tags '(core after-change)
-;; (emojify-tests-with-emojified-buffer ":smile:\n:)"
-;; (emacs-lisp-mode)
-;; (emojify-redisplay-emojis-in-region)
-;; (emojify-mode +1)
-;; (emojify-tests-should-not-be-emojified (line-beginning-position))
-;; (emojify-tests-should-not-be-emojified (line-beginning-position 2))
-
-;; (comment-region (point-min) (point-max))
-;; (emojify-tests-should-be-emojified (+ 3 (line-beginning-position)))
-;; (emojify-tests-should-be-emojified (+ 3 (line-beginning-position 2)))
-
-;; (uncomment-region (point-min) (point-max))
-;; (emojify-tests-should-not-be-emojified (line-beginning-position))
-;; (emojify-tests-should-not-be-emojified (line-beginning-position 2))))
-
-;; (ert-deftest emojify-tests-emojifying-on-typing ()
-;; :tags '(core after-change)
-;; (emojify-tests-with-emojified-buffer ""
-;; (emacs-lisp-mode)
-;; (emojify-redisplay-emojis-in-region)
-;; (emojify-mode +1)
-;; (emojify-insert-string "; :)")
-;; (emojify-tests-should-be-emojified 4)
-;; (newline)
-;; (emojify-insert-string "; :smile")
-;; (emojify-tests-should-not-be-emojified (+ 4 (line-beginning-position)))
-;; (emojify-insert-string ":")
-;; (emojify-tests-should-be-emojified (+ 4 (line-beginning-position)))))
+ (let ((emojify-user-emojis emojify-test-custom-emojis))
+ (emojify-set-emoji-data)
+ (emojify-tests-with-emojified-static-buffer "😉\n:D\nD:\n:smile:\n:neckbeard:"
+ (emojify-tests-should-be-emojified (point-min))
+ (emojify-tests-should-be-emojified (line-beginning-position 2))
+ (emojify-tests-should-be-emojified (line-beginning-position 3))
+ (emojify-tests-should-be-emojified (line-beginning-position 4))
+ (emojify-tests-should-be-emojified (line-beginning-position 5)))))
+
+(ert-deftest emojify-tests-emojifying-on-comment-uncomment ()
+ :tags '(core after-change)
+ (emojify-tests-with-emojified-buffer ":smile:\n:)"
+ (emacs-lisp-mode)
+ (emojify-redisplay-emojis-in-region)
+ (emojify-mode +1)
+ (emojify-tests-should-not-be-emojified (line-beginning-position))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 2))
+
+ (comment-region (point-min) (point-max))
+ (emojify-redisplay)
+ (emojify-tests-should-be-emojified (+ 3 (line-beginning-position)))
+ (emojify-tests-should-be-emojified (+ 3 (line-beginning-position 2)))
+
+ (uncomment-region (point-min) (point-max))
+ (emojify-redisplay)
+ (emojify-tests-should-not-be-emojified (line-beginning-position))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 2))))
+
+(ert-deftest emojify-tests-emojifying-on-typing ()
+ :tags '(core after-change)
+ (emojify-tests-with-emojified-buffer ""
+ (emacs-lisp-mode)
+ (emojify-redisplay-emojis-in-region)
+ (emojify-mode +1)
+ (emojify-insert-string "; :)")
+ (emojify-redisplay)
+ (emojify-tests-should-be-emojified 4)
+ (newline)
+ (emojify-insert-string "; :smile")
+ (emojify-tests-should-not-be-emojified (+ 4 (line-beginning-position)))
+ (emojify-insert-string ":")
+ (emojify-redisplay)
+ (emojify-tests-should-be-emojified (+ 4 (line-beginning-position)))))
(ert-deftest emojify-tests-emoji-uncovering ()
:tags '(behaviour point-motion)
@@ -114,6 +138,12 @@
:tags '(behaviour point-motion)
(emojify-tests-with-emojified-buffer " :)"
(with-mock
+ ;; Since emojify checks that there is no message being displayed
+ ;; before echoing the emoji, we need to stub out current-message
+ ;; too otherwise emojify does not echo the message since messages
+ ;; from other tests are being displayed
+ (unless noninteractive
+ (stub current-message => nil))
(mock (message ":)"))
(setq emojify-point-entered-behaviour 'echo)
(goto-char (1+ (point-min)))
@@ -132,35 +162,55 @@
(ert-deftest emojify-tests-emojify-setting-styles ()
:tags '(styles github ascii)
- (emojify-tests-with-emojified-static-buffer ":) 😄 :smile:"
+ (emojify-tests-with-emojified-static-buffer ":) 😄 :smile: return"
(let ((ascii-emoji-pos (point-min))
(unicode-emoji-pos (+ (point-min) (length ":) ")))
- (github-emoji-pos (+ (point-min) (length ":) 😄 "))))
+ (github-emoji-pos (+ (point-min) (length ":) 😄 ")))
+ (prettify-emoji-pos (+ (point-min) (length ":) 😄 :smile: "))))
+
+ (setq prettify-symbols-alist
+ '(("return" . ?↪)))
+
+ (when (fboundp 'prettify-symbols-mode)
+ (prettify-symbols-mode +1))
(emojify-set-emoji-styles '(ascii))
(emojify-tests-should-be-emojified ascii-emoji-pos)
(emojify-tests-should-not-be-emojified unicode-emoji-pos)
(emojify-tests-should-not-be-emojified github-emoji-pos)
+ (emojify-tests-should-not-be-emojified prettify-emoji-pos)
(emojify-set-emoji-styles '(unicode))
(emojify-tests-should-not-be-emojified ascii-emoji-pos)
(emojify-tests-should-be-emojified unicode-emoji-pos)
(emojify-tests-should-not-be-emojified github-emoji-pos)
+ (emojify-tests-should-not-be-emojified prettify-emoji-pos)
(emojify-set-emoji-styles '(github))
(emojify-tests-should-not-be-emojified ascii-emoji-pos)
(emojify-tests-should-not-be-emojified unicode-emoji-pos)
(emojify-tests-should-be-emojified github-emoji-pos)
+ (emojify-tests-should-not-be-emojified prettify-emoji-pos)
- (emojify-set-emoji-styles '(ascii unicode github))
+ (emojify-set-emoji-styles '(prettify-symbol))
+ (emojify-tests-should-not-be-emojified ascii-emoji-pos)
+ (emojify-tests-should-not-be-emojified unicode-emoji-pos)
+ (emojify-tests-should-not-be-emojified github-emoji-pos)
+ (when (fboundp 'prettify-symbols-mode)
+ (emojify-tests-should-be-emojified prettify-emoji-pos))
+
+ (emojify-set-emoji-styles '(ascii unicode github prettify-symbol))
(emojify-tests-should-be-emojified ascii-emoji-pos)
(emojify-tests-should-be-emojified unicode-emoji-pos)
(emojify-tests-should-be-emojified github-emoji-pos)
+ (when (fboundp 'prettify-symbols-mode)
+ (emojify-tests-should-be-emojified prettify-emoji-pos))
(emojify-set-emoji-styles nil)
(emojify-tests-should-not-be-emojified ascii-emoji-pos)
(emojify-tests-should-not-be-emojified unicode-emoji-pos)
- (emojify-tests-should-not-be-emojified github-emoji-pos))))
+ (emojify-tests-should-not-be-emojified github-emoji-pos)
+ (emojify-tests-should-not-be-emojified prettify-emoji-pos))))
(ert-deftest emojify-tests-program-contexts ()
:tags '(core prog contextual)
@@ -335,7 +385,7 @@
(fundamental-mode)
(let ((count 0))
(emojify-do-for-emojis-in-region (point-min) (point-max)
- (incf count))
+ (cl-incf count))
;; Only one emoji should be displayed
(should (= count 1))
;; The larger emoji should be preferred
@@ -484,6 +534,113 @@
(delete-selection-pre-hook))
(should (equal (point-min) (point-max))))))
+(ert-deftest emojify-tests-prettify-symbols ()
+ :tags '(prettify-symbols)
+ (when (fboundp 'prettify-symbols-mode)
+ (emojify-tests-with-emojified-static-buffer "try:
+ x = 1
+except:
+ raise(Exception)
+
+yield 3
+return 4
+"
+ (emojify-set-emoji-styles '(ascii unicode github prettify-symbol))
+ (python-mode)
+ (setq prettify-symbols-alist
+ '(("return" . ?↪)
+ ("try" . ?😱)
+ ("except" . ?⛐)
+ ("raise" . ?💥)))
+ (emojify-tests-should-not-be-emojified (point-min))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 3))
+ (emojify-tests-should-not-be-emojified (+ (line-beginning-position 4) 5))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 6))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 7))
+ (prettify-symbols-mode +1)
+ (emojify-tests-should-be-emojified (point-min))
+ (should (equal (get-text-property (point-min) 'emojify-text) "try"))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 3))
+ (emojify-tests-should-be-emojified (+ (line-beginning-position 4) 5))
+ (should (equal (get-text-property (+ (line-beginning-position 4) 5) 'emojify-text) "raise"))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 6))
+ (emojify-tests-should-be-emojified (line-beginning-position 7))
+ (should (equal (get-text-property (line-beginning-position 7) 'emojify-text) "return"))
+ (prettify-symbols-mode -1)
+ (emojify-tests-should-not-be-emojified (point-min))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 3))
+ (emojify-tests-should-not-be-emojified (+ (line-beginning-position 4) 5))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 6))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 7)))))
+
+(ert-deftest emojify-tests-prettify-symbols-with-custom-images ()
+ :tags '(prettify-symbols)
+ (when (fboundp 'prettify-symbols-mode)
+ (let ((emojify-user-emojis emojify-test-custom-emojis))
+ (emojify-set-emoji-data)
+ (emojify-tests-with-emojified-static-buffer "try:
+ lambda x: x
+except:
+ raise(Exception)
+
+yield 3
+return 4
+"
+ (emojify-set-emoji-styles '(ascii unicode github prettify-symbol))
+ (python-mode)
+ (setq prettify-symbols-alist
+ '(("return" . ?↪)
+ ("try" . ?😱)
+ ("except" . ?⛐)
+ ("lambda" . ?λ)
+ ("raise" . ?💥)))
+ (emojify-tests-should-not-be-emojified (+ (line-beginning-position 2) 5))
+ (prettify-symbols-mode +1)
+ (emojify-tests-should-be-emojified (point-min))
+ (emojify-tests-should-be-emojified (+ (line-beginning-position 2) 5))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 3))
+ (emojify-tests-should-be-emojified (+ (line-beginning-position 4) 5))
+ (emojify-tests-should-not-be-emojified (line-beginning-position 6))
+ (emojify-tests-should-be-emojified (line-beginning-position 7))))))
+
+(ert-deftest emojify-tests-apropos ()
+ :tags '(apropos)
+ (emojify-apropos-emoji "squi")
+ ;; Window with results should be visible
+ (with-mock
+ (stub message => nil)
+ (should (get-buffer-window emojify-apropos-buffer-name))
+ (let ((matches 0))
+
+ (with-current-buffer emojify-apropos-buffer-name
+ ;; Force a display of emojis
+ (emojify-redisplay-emojis-in-region (point-min) (point-max))
+ (emojify-do-for-emojis-in-region (point-min) (point-max)
+ (goto-char emoji-start)
+ (call-interactively #'emojify-apropos-copy-emoji)
+ (should (string= (car kill-ring) (get-text-property (point) 'emojify-text)))
+ (cl-incf matches)))
+
+ (should (= matches 2)))
+
+ ;; Test with custom emoji
+ (let ((emojify-user-emojis emojify-test-custom-emojis)
+ (matches 0))
+
+ (emojify-set-emoji-data)
+ (emojify-apropos-emoji "lambda")
+ (should (get-buffer-window emojify-apropos-buffer-name))
+
+ (with-current-buffer emojify-apropos-buffer-name
+ (emojify-redisplay-emojis-in-region (point-min) (point-max))
+ (emojify-do-for-emojis-in-region (point-min) (point-max)
+ (goto-char emoji-start)
+ (call-interactively #'emojify-apropos-copy-emoji)
+ (should (string= (car kill-ring) (get-text-property (point) 'emojify-text)))
+ (cl-incf matches)))
+
+ (should (= matches 1)))))
+
(ert-deftest emojify-tests-no-byte-compilation-warnings ()
:tags '(byte-compilation)
(with-mock
diff --git a/test/test-helper.el b/test/test-helper.el
@@ -22,19 +22,32 @@
;; Libs required for tests
(require 'ert)
(require 'el-mock)
-(require 'cl)
+(require 'cl-lib)
(require 'noflet)
;; Load emojify
(require 'emojify)
+;; Define custom emoji config
+(defvar emojify-test-custom-emojis)
+(let* ((project-dir (locate-dominating-file (or (buffer-file-name) load-file-name)
+ ".cask"))
+ (custom-emoji-dir (expand-file-name "test/assets/" project-dir)))
+ (setq emojify-test-custom-emojis
+ `((":troll:" . (("name" . "Troll") ("image" . ,(expand-file-name "trollface.png" custom-emoji-dir)) ("style" . "github")))
+ (":neckbeard:" . (("name" . "Neckbeard") ("image" . ,(expand-file-name "neckbeard.png" custom-emoji-dir)) ("style" . "github")))
+ ("λ" . (("name" . "Lambda") ("image" . ,(expand-file-name "lambda.png" custom-emoji-dir)) ("style" . "unicode"))))))
+
;; Helper macros for tests
(defmacro emojify-tests-with-saved-customizations (&rest forms)
- "Run forms saving current customizations and restoring them on completion.
+ "Run FORMS saving current customizations and restoring them on completion.
Helps isolate tests from each other's customizations."
(declare (indent 0))
`(let ((emojify-saved-emoji-json emojify-emoji-json)
+ (emojify-saved-user-emojis emojify-user-emojis)
+ (emojify-saved-user-emojis-parsed emojify--user-emojis)
+ (emojify-saved-emojify-regexps emojify-regexps)
(emojify-saved-display-style emojify-display-style)
(emojify-saved-inhibit-major-modes emojify-inhibit-major-modes)
(emojify-saved-inhibit-in-buffer-functions emojify-inhibit-in-buffer-functions)
@@ -53,6 +66,9 @@ Helps isolate tests from each other's customizations."
(setq emojify-emoji-json emojify-saved-emoji-json
emojify-display-style emojify-saved-display-style
emojify-inhibit-major-modes emojify-saved-inhibit-major-modes
+ emojify-user-emojis emojify-saved-user-emojis
+ emojify--user-emojis emojify-saved-user-emojis-parsed
+ emojify-regexps emojify-saved-emojify-regexps
emojify-inhibit-in-buffer-functions emojify-saved-inhibit-in-buffer-functions
emojify-program-contexts emojify-saved-program-contexts
emojify-inhibit-functions emojify-saved-inhibit-functions
@@ -64,6 +80,9 @@ Helps isolate tests from each other's customizations."
(emojify-set-emoji-styles emojify-saved-emoji-style))))
(defmacro emojify-tests-with-emojified-buffer (str &rest forms)
+ "Create a buffer with STR and execute FORMS.
+
+The FORMS are executed with emojify enabled."
(declare (indent 1))
;; Run tests in a new buffer
`(let ((test-buffer (get-buffer-create " *emojify-test-buffer*")))
@@ -90,12 +109,17 @@ Helps isolate tests from each other's customizations."
(kill-buffer test-buffer))))))
(defmacro emojify-tests-with-emojified-static-buffer (str &rest forms)
+ "Create a buffer with STR and execute FORMS.
+
+All kinds of dynamic behaviour on buffer are disabled. See
+`emojify-with-saved-buffer-state'"
(declare (indent 1))
`(emojify-tests-with-emojified-buffer ,str
(emojify-with-saved-buffer-state
,@forms)))
(defmacro emojify-tests-should-be-emojified (point)
+ "Assert there is an emoji at POINT."
`(progn
(should-not (get-text-property ,point 'point-left))
(should (get-text-property ,point 'emojified))
@@ -108,6 +132,7 @@ Helps isolate tests from each other's customizations."
(should (get-text-property ,point 'point-entered))))
(defmacro emojify-tests-should-not-be-emojified (point)
+ "Assert there is not emoji at POINT."
`(progn
(should-not (get-text-property ,point 'point-left))
(should-not (get-text-property ,point 'emojified))
@@ -120,6 +145,7 @@ Helps isolate tests from each other's customizations."
(should-not (get-text-property ,point 'point-entered))))
(defmacro emojify-tests-should-be-uncovered (point)
+ "Assert the emoji at POINT is uncovered."
`(progn
(should (get-text-property ,point 'point-left))
(should (get-text-property ,point 'emojified))
@@ -131,9 +157,19 @@ Helps isolate tests from each other's customizations."
(should-not (get-text-property ,point 'display))))
(defun emojify-insert-string (string)
+ "Insert the STRING."
(mapc (lambda (character)
(insert character))
(string-to-vector string)))
+(defun emojify-redisplay ()
+ "Trigger a redisplay."
+ (if noninteractive
+ ;; In noninteractive mode JIT is not called
+ ;; call it
+ (jit-lock-fontify-now)
+ ;; In interactive mode just force redisplay
+ (redisplay t)))
+
(provide 'test-helper)
;;; test-helper.el ends here