From 0e2a46c01ad412d59b3a67a4ba8292914e700102 Mon Sep 17 00:00:00 2001
From: Shankar Rao <shankar.rao@gmail.com>
Date: Wed, 24 Jun 2020 13:03:04 +0200
Subject: [PATCH] Add mode for automatically unhiding emphasis markers for text
 at point

* lisp/org.el:
(org-auto-emphasis-unhide-at-point): Parameter that controls the
behavior of Org Auto Emphasis mode.  It can be one of the values nil, t, and
'right-edge, and works similarly to the parameter
`prettify-symbols-unprettify-at-point' for `prettify-symbols-mode'.
(org-do-emphasis-faces): When hiding emphasis markers, add additional
text properties 'org-emph-start and org-emph-end to the emphasized
region.
(org-auto-emphasis--current-region-bounds): Local variable containing
the bounds of the region whose emphasis markers are currently
unhidden.
(org-auto-emphasis--get-prop-as-list): Helper function that returns
Org Auto Emphasis properties as a list.
(org-auto-emphasis--post-command-hook): Function added to
`post-command-hook' that rehides emphasis markers for the previous
region and unhides emphasis marks for the current region.
(org-auto-emphasis-mode): Toggles Org Auto Emphasis mode.  Can be
added to `org-mode-hook' to be enabled for all org-mode files.

This code was adapted from prettify-symbols-mode in prog-mode.el
---
 lisp/org.el | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 83 insertions(+), 4 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 4d46b4173..8bab33ec0 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -3644,6 +3644,19 @@ following symbols:
   :type 'boolean
   :safe #'booleanp)
 
+(defcustom org-auto-emphasis-unhide-at-point nil
+  "If non-nil, unhide the emphasis markers for the text at point.
+If set to the symbol `right-edge', also unhide the emphasis
+markers if point is immediately after the emphasized text.  The
+emphasis markers will be rehidden as soon as point moves away
+from the emphasized text.  If set to nil, the emphasis markers
+always remain hidden."
+  :package-version '(Org . "9.5")
+  :type '(choice (const :tag "Never unhide emphasis markers" nil)
+                 (const :tag "Unhide when point is inside" t)
+                 (const :tag "Unhide when point is inside or at right edge" right-edge))
+  :group 'org-appearance)
+
 (defcustom org-hide-macro-markers nil
   "Non-nil mean font-lock should hide the brackets marking macro calls."
   :group 'org-appearance
@@ -5055,12 +5068,78 @@ stacked delimiters is N.  Escaping delimiters is not possible."
 				   '(font-lock-multiline t org-emphasis t))
 	      (when (and org-hide-emphasis-markers
 			 (not (org-at-comment-p)))
-		(add-text-properties (match-end 4) (match-beginning 5)
-				     '(invisible org-link))
-		(add-text-properties (match-beginning 3) (match-end 3)
-				     '(invisible org-link)))
+		(let ((s1 (match-beginning 3))
+		      (e1 (match-end 3))
+		      (s2 (match-end 4))
+		      (e2 (match-beginning 5)))
+		  (add-text-properties s2 e2 '(invisible org-link))
+		  (add-text-properties s1 e1 '(invisible org-link))
+		  (add-text-properties s1 e2
+				       `(org-emph-start ,s1 org-emph-end ,e2))))
 	      (throw :exit t))))))))
 
+(defvar-local org-auto-emphasis--current-region-bounds nil)
+
+(defun org-auto-emphasis--get-prop-as-list (prop)
+  "Helper function to get org-auto-emphasis properties as a list.
+If `org-auto-emphasis-unhide-at-point' is set to t, then return
+the text property PROP at point in a list. If
+`org-auto-emphasis-unhide-at-point' is set to `right-edge', the
+also include the text property PROP at point-1 unless we are at
+the beginning of the buffer."
+  (remove nil
+	  (list (get-text-property (point) prop)
+		(when (and (eq org-auto-emphasis-unhide-at-point 'right-edge)
+			   (not (bobp)))
+		  (get-text-property (1- (point)) prop)))))
+
+(defun org-auto-emphasis--post-command-hook ()
+  ;; Rehide emphasis markers for the previous region.
+  (when (and org-auto-emphasis--current-region-bounds
+	     (or (< (point) (car org-auto-emphasis--current-region-bounds))
+		 (> (point) (cadr org-auto-emphasis--current-region-bounds))
+		 (and (not (eq org-auto-emphasis-unhide-at-point 'right-edge))
+		      (= (point) (cadr org-auto-emphasis--current-region-bounds)))))
+	(apply #'font-lock-flush org-auto-emphasis--current-region-bounds)
+	(setq org-auto-emphasis--current-region-bounds nil))
+  ;; Unhide emphasis markers for the current region.
+  (let* ((s (org-auto-emphasis--get-prop-as-list 'org-emph-start))
+	 (e (and s (org-auto-emphasis--get-prop-as-list 'org-emph-end)))
+	 (s (and e (apply #'min s)))
+	 (e (and s (apply #'max e))))
+    (when e
+      (with-silent-modifications
+	(setq org-auto-emphasis--current-region-bounds (list s e))
+	(remove-text-properties s (1+ s) '(invisible org-link))
+	(remove-text-properties (1- e) e '(invisible org-link))))))
+
+(define-minor-mode org-auto-emphasis-mode
+  "Toggle Org Auto Emphasis mode.
+This mode, when enabled, unhides emphasis markers for the text
+at point, depending on the value of
+`org-auto-emphasis-unhide-at-point'. With a prefix argument ARG,
+enable Org Auto Emphasis mode if ARG is positive, and disable it
+otherwise.  If called from Lisp, enable the mode if ARG is
+omitted or nil.
+
+To enable this in all Org files, add the following line to init.el:
+
+  (add-hook \\='org-mode #\\='org-auto-emphasis-mode)
+"
+  :init-value nil
+  (if org-auto-emphasis-mode
+      ;; Turn on
+      (progn
+        (setq-local font-lock-extra-managed-props
+                    (append font-lock-extra-managed-props
+                            '(org-emph-start org-emph-end)))
+        (when org-auto-emphasis-unhide-at-point
+          (add-hook 'post-command-hook
+                    #'org-auto-emphasis--post-command-hook nil t))
+        (font-lock-flush))
+    ;; Turn off
+    (remove-hook 'post-command-hook #'org-auto-emphasis--post-command-hook t)))
+
 (defun org-emphasize (&optional char)
   "Insert or change an emphasis, i.e. a font like bold or italic.
 If there is an active region, change that region to a new emphasis.
-- 
2.17.1

