;;; eaf-org-previewer.el --- Org previewer ;; Filename: eaf-org-previewer.el ;; Description: Org previewer ;; Author: Andy Stewart ;; Maintainer: Andy Stewart ;; Copyright (C) 2021, Andy Stewart, all rights reserved. ;; Created: 2021-08-01 10:23:59 ;; Version: 0.1 ;; Last-Updated: 2021-08-01 10:23:59 ;; By: Andy Stewart ;; URL: http://www.emacswiki.org/emacs/download/eaf-org-previewer.el ;; Keywords: ;; Compatibility: GNU Emacs 28.0.50 ;; ;; Features that might be required by this library: ;; ;; ;; ;;; This file is NOT part of GNU Emacs ;;; License ;; ;; 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, 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; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Org previewer ;; ;;; Installation: ;; ;; Put eaf-org-previewer.el to your load-path. ;; The load-path is usually ~/elisp/. ;; It's set in your ~/.emacs like this: ;; (add-to-list 'load-path (expand-file-name "~/elisp")) ;; ;; And the following to your ~/.emacs startup file. ;; ;; (require 'eaf-org-previewer) ;; ;; No need more. ;;; Customize: ;; ;; ;; ;; All of the above can customize by: ;; M-x customize-group RET eaf-org-previewer RET ;; ;;; Change log: ;; ;; 2021/08/01 ;; * First released. ;; ;;; Acknowledgements: ;; ;; ;; ;;; TODO ;; ;; ;; ;;; Require (require 'ox-html) ;;; Code: (defcustom eaf-org-previewer-keybinding '(("C--" . "zoom_out") ("C-=" . "zoom_in") ("C-0" . "zoom_reset") ("C-s" . "search_text_forward") ("C-r" . "search_text_backward") ("C-n" . "scroll_up") ("C-p" . "scroll_down") ("C-f" . "scroll_right") ("C-b" . "scroll_left") ("C-v" . "scroll_up_page") ("C-y" . "yank_text") ("C-w" . "kill_text") ("M-e" . "atomic_edit") ("M-c" . "caret_toggle_browsing") ("M-D" . "select_text") ("M-s" . "open_link") ("M-S" . "open_link_new_buffer") ("M-B" . "open_link_background_buffer") ("C-/" . "undo_action") ("M-_" . "redo_action") ("M-w" . "copy_text") ("M-f" . "history_forward") ("M-b" . "history_backward") ("M-q" . "clear_cookies") ("C-t" . "toggle_password_autofill") ("C-d" . "save_page_password") ("M-a" . "toggle_adblocker") ("C-M-q" . "clear_history") ("C-M-i" . "import_chrome_history") ("M-v" . "scroll_down_page") ("M-<" . "scroll_to_begin") ("M->" . "scroll_to_bottom") ("M-p" . "duplicate_page") ("M-t" . "new_blank_page") ("M-d" . "toggle_dark_mode") ("SPC" . "insert_or_scroll_up_page") ("J" . "insert_or_select_left_tab") ("K" . "insert_or_select_right_tab") ("j" . "insert_or_scroll_up") ("k" . "insert_or_scroll_down") ("h" . "insert_or_scroll_left") ("l" . "insert_or_scroll_right") ("f" . "insert_or_open_link") ("F" . "insert_or_open_link_new_buffer") ("B" . "insert_or_open_link_background_buffer") ("c" . "insert_or_caret_at_line") ("u" . "insert_or_scroll_down_page") ("d" . "insert_or_scroll_up_page") ("H" . "insert_or_history_backward") ("L" . "insert_or_history_forward") ("t" . "insert_or_new_blank_page") ("T" . "insert_or_recover_prev_close_page") ("i" . "insert_or_focus_input") ("I" . "insert_or_open_downloads_setting") ("r" . "insert_or_refresh_page") ("g" . "insert_or_scroll_to_begin") ("x" . "insert_or_close_buffer") ("G" . "insert_or_scroll_to_bottom") ("-" . "insert_or_zoom_out") ("=" . "insert_or_zoom_in") ("0" . "insert_or_zoom_reset") ("m" . "insert_or_save_as_bookmark") ("o" . "insert_or_open_browser") ("y" . "insert_or_download_youtube_video") ("Y" . "insert_or_download_youtube_audio") ("p" . "insert_or_toggle_device") ("P" . "insert_or_duplicate_page") ("1" . "insert_or_save_as_pdf") ("2" . "insert_or_save_as_single_file") ("3" . "insert_or_save_as_screenshot") ("v" . "insert_or_view_source") ("e" . "insert_or_edit_url") ("n" . "insert_or_export_text") ("," . "insert_or_switch_to_reader_mode") ("." . "insert_or_translate_text") (";" . "insert_or_translate_page") ("C-M-c" . "copy_code") ("C-M-l" . "copy_link") ("C-a" . "select_all_or_input_text") ("M-u" . "clear_focus") ("C-j" . "open_downloads_setting") ("M-o" . "eval_js") ("M-O" . "eval_js_file") ("" . "eaf-browser-send-esc-or-exit-fullscreen") ("M-," . "eaf-send-down-key") ("M-." . "eaf-send-up-key") ("M-m" . "eaf-send-return-key") ("" . "refresh_page") ("" . "open_devtools") ("" . "eaf-send-ctrl-return-sequence") ) "The keybinding of EAF Org Previewer." :type 'cons) (defcustom eaf-org-dark-mode "follow" "" :type 'string) (defcustom eaf-org-extension-list '("org") "The extension list of org previewer application." :type 'cons) (defvar eaf-org-file-list '()) (defvar eaf-org-killed-file-list '()) (defun eaf--org-delete-preview-file (org-file) "Delete the org-preview file when given ORG-FILE name." (let ((org-html-file (concat (file-name-sans-extension org-file) ".html"))) (when (file-exists-p org-html-file) (delete-file org-html-file) (message "[EAF] Cleaned org-preview file %s (%s)." org-html-file org-file)))) (defun eaf--org-killed-buffer-clean () "Function cleaning the killed org buffer." (dolist (org-killed-buffer eaf-org-killed-file-list) (unless (get-file-buffer org-killed-buffer) (setq eaf-org-file-list (remove org-killed-buffer eaf-org-file-list)) (eaf--org-delete-preview-file org-killed-buffer))) (setq eaf-org-killed-file-list nil)) (defun eaf--org-preview-monitor-kill () "Function monitoring when org-preview application is killed." ;; Because save org buffer will trigger `kill-buffer' action, ;; but org buffer still live after do `kill-buffer' action. ;; So I run a timer to check org buffer is live after `kill-buffer' action. (when (member (buffer-file-name) eaf-org-file-list) (unless (member (buffer-file-name) eaf-org-killed-file-list) (push (buffer-file-name) eaf-org-killed-file-list)) (run-with-timer 1 nil (lambda () (eaf--org-killed-buffer-clean))))) (defun eaf--org-preview-monitor-buffer-save () "Save org-preview buffer." (when (eaf-epc-live-p eaf-epc-process) (ignore-errors ;; eaf-org-file-list? (org-html-export-to-html) (eaf-call-async "update_buffer_with_url" eaf-org-previewer-module-path (buffer-file-name) "") (message "[EAF] Export %s to HTML." (buffer-file-name))))) (defun eaf--org-preview-display (buf) "Given BUF, split window to show file and previewer." (let ((url (buffer-local-value 'eaf--buffer-url buf))) ;; Find file first, because `find-file' will trigger `kill-buffer' operation. (save-excursion (find-file url) (org-html-export-to-html) (add-hook 'after-save-hook #'eaf--org-preview-monitor-buffer-save nil t) (add-hook 'kill-buffer-hook #'eaf--org-preview-monitor-kill nil t)) ;; Add file name to `eaf-org-file-list' after command `find-file'. (unless (member url eaf-org-file-list) (push url eaf-org-file-list)) ;; Split window to show file and previewer. (eaf-split-preview-windows url) ;; Switch to new buffer if buffer create successful. (switch-to-buffer buf) (other-window +1))) (add-hook 'eaf-stop-process-hook #'(lambda () (dolist (org-file-name eaf-org-file-list) (eaf--org-delete-preview-file org-file-name)) (setq eaf-org-file-list nil) (setq eaf-org-killed-file-list nil))) (add-to-list 'eaf-app-binding-alist '("org-previewer" . eaf-org-previewer-keybinding)) (setq eaf-org-previewer-module-path (concat (file-name-directory load-file-name) "buffer.py")) (add-to-list 'eaf-app-module-path-alist '("org-previewer" . eaf-org-previewer-module-path)) (add-to-list 'eaf-preview-display-function-alist '("org-previewer" . eaf--org-preview-display)) (add-to-list 'eaf-app-extensions-alist '("org-previewer" . eaf-org-extension-list)) (provide 'eaf-org-previewer) ;;; eaf-org-previewer.el ends here