emacs-emojify

fork of https://github.com/iqbalansari/emacs-emojify
Log | Files | Refs | LICENSE

commit 95f2d65e9917d310a269b9e7d01e0b6d40129660
parent 2fbbb4b6c85d6171d0c33512e203a59e90309127
Author: Iqbal Ansari <iqbalansari02@yahoo.com>
Date:   Mon, 26 Oct 2015 23:30:25 +0530

Minor cleanups

Diffstat:
Memojify.el | 128+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
1 file changed, 79 insertions(+), 49 deletions(-)

diff --git a/emojify.el b/emojify.el @@ -22,9 +22,13 @@ (string-match-p "\\*helm" (buffer-name)) (memq major-mode '(doc-view-mode pdf-view-mode image-mode))))) -;; Can be one of image, unicode, ascii +;; (Eventually) Can be one of image, unicode, ascii (defvar emoji-substitution-style 'image) + + +;; Core functions and macros + (defsubst emojify-get-image (name) (let ((emoji-one (gethash name emoji-parsed))) (when emoji-one @@ -38,22 +42,24 @@ ;; no-op if imagemagick is not available :height (default-font-height))))) -(defun emojify--setup-emoji-display (start end match) - "Emojify the text between start and end" +;; TODO Inline this, this depends too much on the state maintained in `emojify-display-emojis-in-region' +;; to make sense as a standalone function +(defun emojify--setup-emoji-display (beg end match) + "Emojify the text between BEG and END, MATCH is the string to highlighted." ;; TODO Generalize these into a set of predicates (when (and (or (not (derived-mode-p 'prog-mode)) ;; TODO: How (in)efficient is this (and (save-excursion - (goto-char start) + (goto-char beg) (nth 8 (syntax-ppss))) (save-excursion (goto-char end) (nth 8 (syntax-ppss))))) - (or (not (char-before start)) + (or (not (char-before beg)) ;; 32 space since ? i.e. (? followed by a space is not readable) ;; 34 is " since?" confuses font-lock ;; ?> Think multiline comments - (memq (char-syntax (char-before start)) '(32 34 ?- ?< ?>))) + (memq (char-syntax (char-before beg)) '(32 34 ?- ?< ?>))) (or (not (char-after end)) ;; 32 space since ? i.e. (? followed by a space is not readable) ;; 34 is " since?" confuses font-lock @@ -62,45 +68,67 @@ (equal (face-at-point) 'org-tag))) ;; TODO: Remove double checks (when (gethash match emoji-parsed) - (add-text-properties start end (list 'display (pcase emoji-substitution-style + (add-text-properties beg end (list 'display (pcase emoji-substitution-style (`image (emojify-get-image match))) 'emojified t 'point-entered (lambda (x y) (message (format "%s" match)))))))) -(defun emojify--emojify-region (beg end) - (let ((inhibit-point-motion-hooks t)) - (with-silent-modifications - (save-match-data - (save-excursion - (goto-char beg) - (while (search-forward-regexp emoji-regexps end t) - (emojify--setup-emoji-display (match-beginning 0) - (match-end 0) - (match-string 0)))))))) - -(defun emojify--unemojify-region (beg end &optional point-entered-p) - (let ((inhibit-point-motion-hooks t)) - (with-silent-modifications - (save-match-data - (save-excursion - (while (< beg end) - (let* ((emoji-start (text-property-any beg end 'emojified t)) - (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 some text with emoji property - (when emoji-start - (remove-text-properties emoji-start emoji-end (append (list 'emojified t - 'display t - 'composition t - 'point-entered t) - (unless point-entered-p - '(point-left t))))) - (setq beg emoji-end)))))))) - -(defun emojify--after-change-function (beginning end len) +(defmacro emojify-with-saved-buffer-state (&rest forms) + "Execute FORMS saving global point and mark, match-data, buffer modification state +also inhibit buffer change, point motion hooks. + +Used by `emojify-display-emojis-in-region' and `emojify-undisplay-emojis-in-region'" + (declare (debug t) (indent 0)) + `(let ((inhibit-point-motion-hooks t)) + (with-silent-modifications + (save-match-data + (save-excursion + ,@forms))))) + +(defun emojify-display-emojis-in-region (beg end) + "Display emojis in region. +BEG and END are the beginning and end of the region respectively" + (emojify-with-saved-buffer-state + (goto-char beg) + (while (search-forward-regexp emoji-regexps end t) + (emojify--setup-emoji-display (match-beginning 0) + (match-end 0) + (match-string 0))))) + +(defun emojify-undisplay-emojis-in-region (beg end &optional point-entered-p) + "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 composition or other + ;; properties inserted by other packages. This might fails too if a + ;; package adds any of these properties between an emojified text, but + ;; that situation is hopefully very rare and 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 + 'composition t + 'point-entered t) + ;; Get this working + (unless point-entered-p + '(point-left t))))) + ;; Setup the next iteration + (setq beg emoji-end))))) + +(defun emojify-after-change-function (beginning end len) (let ((inhibit-read-only t) ;; Extend the region to match the beginning of line where change began (region-end (save-excursion @@ -111,9 +139,11 @@ (goto-char beginning) (line-beginning-position)))) ;; Remove previously added emojis - (emojify--unemojify-region region-start region-end) + (emojify-undisplay-emojis-in-region region-start region-end) ;; Add emojis to the region - (emojify--emojify-region region-start region-end))) + (emojify-display-emojis-in-region region-start region-end))) + + ;; Resize emojis on text resize ;; (defadvice text-scale-increase (after emojify-resize-emojis (&rest ignored)) @@ -124,22 +154,22 @@ (defun emojify-turn-on-emojify-mode () (when (emojify--emojify-buffer-p) (if font-lock-defaults - (jit-lock-register #'emojify--emojify-region) + (jit-lock-register #'emojify-display-emojis-in-region) (save-restriction (widen) - (emojify--emojify-region (point-min) (point-max)))) + (emojify-display-emojis-in-region (point-min) (point-max)))) ;; Make sure emojis are displayed in newly inserted text - (add-hook 'after-change-functions #'emojify--after-change-function t t))) + (add-hook 'after-change-functions #'emojify-after-change-function t t))) (defun emojify-turn-off-emojify-mode () (when (emojify--emojify-buffer-p) ;; Remove currently displayed emojis (save-restriction (widen) - (emojify--unemojify-region (point-min) (point-max))) - (jit-lock-unregister #'emojify--emojify-region) + (emojify-undisplay-emojis-in-region (point-min) (point-max))) + (jit-lock-unregister #'emojify-display-emojis-in-region) ;; Make sure emojis are displayed in newly inserted text - (remove-hook 'after-change-functions #'emojify--after-change-function t))) + (remove-hook 'after-change-functions #'emojify-after-change-function t))) (define-minor-mode emojify-mode "Emojify mode"