From c9b505d022410a481210928ecc4cce1f199ec53b Mon Sep 17 00:00:00 2001 From: Nathaniel Nicandro Date: Thu, 13 Apr 2023 15:06:35 -0500 Subject: [PATCH] Highlight ANSI escape sequences * etc/ORG-NEWS: Describe the new feature. * org.el (org-fontify-ansi-sequences): New customization variable and function which does the work of fontifying the sequences. (org-set-font-lock-defaults): Add the `org-fontify-ansi-sequences` function to the font-lock keywords. (org-ansi-mode): New minor mode to enable/disable highlighting of the sequences. Enabled in Org buffers by default. --- etc/ORG-NEWS | 12 ++++++ lisp/org.el | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index b7c88fd..8690540 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -169,6 +169,18 @@ official [[https://clojure.org/guides/deps_and_cli][Clojure CLI tools]]. The command can be customized with ~ob-clojure-cli-command~. ** New features +*** ANSI escape sequences are now highlighted in the whole buffer + +A new customization ~org-fontify-ansi-sequences~ is available which +tells Org to highlight all ANSI sequences in the buffer if non-nil and +the new minor mode ~org-ansi-mode~ is enabled. + +To disable highlighting of the sequences you can either +disable ~org-ansi-mode~ or set ~org-fontify-ansi-sequences~ to ~nil~ +and =M-x revert-buffer RET=. Doing the latter will disable +highlighting of sequences in all newly opened Org buffers whereas +doing the former disables highlighting locally to the current buffer. + *** Add support for ~logind~ idle time in ~org-user-idle-seconds~ When Emacs is built with =dbus= support and diff --git a/lisp/org.el b/lisp/org.el index 26d2a86..62a5134 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -81,6 +81,7 @@ (eval-when-compile (require 'gnus-sum)) (require 'calendar) (require 'find-func) (require 'format-spec) +(require 'ansi-color) (condition-case nil (load (concat (file-name-directory load-file-name) @@ -3582,6 +3583,12 @@ (defcustom org-fontify-whole-block-delimiter-line t :group 'org-appearance :type 'boolean) +(defcustom org-fontify-ansi-sequences t + "Non-nil means to highlight ANSI escape sequences." + :group 'org-appearance + :type 'boolean + :package-version '(Org . "9.7")) + (defcustom org-highlight-latex-and-related nil "Non-nil means highlight LaTeX related syntax in the buffer. When non-nil, the value should be a list containing any of the @@ -5543,6 +5550,72 @@ (defun org-fontify-extend-region (beg end _old-len) (cons beg (or (funcall extend "end" "]" 1) end))) (t (cons beg end)))))) +(defvar org-ansi-mode) + +(defun org-fontify-ansi-sequences (limit) + "Fontify ANSI sequences." + (when (and org-fontify-ansi-sequences org-ansi-mode) + (let (end) + (while (/= (point) limit) + (cond + ((get-text-property (point) 'src-block) + ;; If point is on a src block, skip over it + (goto-char (next-single-property-change (point) 'src-block nil limit)) + (save-restriction + ;; Prevent moving past limit + (narrow-to-region (point) limit) + (forward-line))) + (t + (setq end (next-single-property-change (point) 'src-block nil limit)) + (let ((src-block-beg (and (get-text-property end 'src-block) end))) + (when src-block-beg + ;; Set the end of the region to be fontified to be the + ;; beginning of the src block when end is not limit + (save-excursion + (goto-char src-block-beg) + (forward-line -1) + (org-skip-whitespace) + (setq end (point)))) + (ansi-color-apply-on-region (point) end t) + ;; Reset the context before every fontification cycle. This + ;; avoids issues where `ansi-color-apply-on-region' attempts to + ;; use an old starting point that may be from a different part + ;; of the buffer, leading to "wrong side of point" errors. + (setq ansi-color-context-region nil) + (goto-char (or src-block-beg end))))))))) + +(defvar org-ansi-colors + '(black red green yellow blue purple cyan white)) + +(defun org-ansi-highlight (beg end seq) + (save-excursion + (goto-char end) + (insert "\e") + (insert "[0m") + (goto-char beg) + (insert "\e") + (insert (format "[%sm" seq)))) + +(defun org-ansi-highlight-foreground (beg end color) + "Highlight the foreground between BEG and END with COLOR." + (interactive + (let ((bounds (car (region-bounds)))) + (list (car bounds) (cdr bounds) + (completing-read "Color: " org-ansi-colors nil t)))) + (let ((n (- (length org-ansi-colors) + (length (memq color org-ansi-colors))))) + (org-ansi-highlight beg end (+ 30 n)))) + +(defun org-ansi-highlight-background (beg end color) + "Highlight the background between BEG and END with COLOR." + (interactive + (let ((bounds (car (region-bounds)))) + (list (car bounds) (cdr bounds) + (completing-read "Color: " org-ansi-colors nil t)))) + (let ((n (- (length org-ansi-colors) + (length (memq color org-ansi-colors))))) + (org-ansi-highlight beg end (+ 40 n)))) + (defun org-activate-footnote-links (limit) "Add text properties for footnotes." (let ((fn (org-footnote-next-reference-or-definition limit))) @@ -5861,6 +5934,7 @@ (defun org-set-font-lock-defaults () ;; Blocks and meta lines '(org-fontify-meta-lines-and-blocks) '(org-fontify-inline-src-blocks) + '(org-fontify-ansi-sequences) ;; Citations. When an activate processor is specified, if ;; specified, try loading it beforehand. (progn @@ -15455,6 +15529,44 @@ (defun org-agenda-prepare-buffers (files) (when org-agenda-file-menu-enabled (org-install-agenda-files-menu)))) + +;;;; ANSI sequences minor mode + +(defvar org-ansi-mode-map (make-sparse-keymap) + "Keymap for the minor `org-ansi-mode'.") + +(org-defkey org-ansi-mode-map (kbd "C-c hf") #'org-ansi-highlight-foreground) +(org-defkey org-ansi-mode-map (kbd "C-c hb") #'org-ansi-highlight-background) + +(define-minor-mode org-ansi-mode + "Toggle the minor `org-ansi-mode'. +This mode adds support to highlight ANSI sequences in Org mode. +The sequences are highlighted only if the customization +`org-fontify-ansi-sequences' is non-nil when the mode is enabled. +\\{org-ansi-mode-map}" + :lighter " OANSI" + (cond + ((and org-fontify-ansi-sequences org-ansi-mode) + (remove-text-properties (point-min) (point-max) '(fontified t)) + (font-lock-ensure)) + (t + (dolist (ov (overlays-in (point-min) (point-max))) + ;; Attempt to find ANSI specific overlays. See + ;; `ansi-color-make-extent'. + (when (eq (car-safe (overlay-get ov 'insert-behind-hooks)) + 'ansi-color-freeze-overlay) + ;; Delete the invisible overlays over the escape sequences + (dolist (ov (overlays-at (1- (overlay-start ov)))) + (when (overlay-get ov 'invisible) + (delete-overlay ov))) + (dolist (ov (overlays-at (1+ (overlay-end ov)))) + (when (overlay-get ov 'invisible) + (delete-overlay ov))) + ;; Delete the overlay over the highlighted text + (delete-overlay ov)))))) + +(add-hook 'org-mode-hook #'org-ansi-mode) + ;;;; CDLaTeX minor mode -- 2.39.1