From 6b9f2a0cf11e531b18c5c7b2f80bd76520677511 Mon Sep 17 00:00:00 2001 From: Daniel Weschke Date: Fri, 14 Jun 2024 12:10:52 +0200 Subject: [PATCH] add org-preview-html --- lisp/org-preview-html/org-preview-html-pkg.el | 15 ++ lisp/org-preview-html/org-preview-html.el | 238 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 lisp/org-preview-html/org-preview-html-pkg.el create mode 100644 lisp/org-preview-html/org-preview-html.el diff --git a/lisp/org-preview-html/org-preview-html-pkg.el b/lisp/org-preview-html/org-preview-html-pkg.el new file mode 100644 index 00000000..eee8162a --- /dev/null +++ b/lisp/org-preview-html/org-preview-html-pkg.el @@ -0,0 +1,15 @@ +(define-package "org-preview-html" "20220809.1033" "Automatically preview org-exported HTML files within Emacs" + '((emacs "25.1") + (org "8.0")) + :commit "785e1f5c99c0f2d76a9a6611a06b4552a343e221" :authors + '(("Jake B" . "jakebox0@protonmail.com")) + :maintainers + '(("Jake B" . "jakebox0@protonmail.com")) + :maintainer + '("Jake B" . "jakebox0@protonmail.com") + :keywords + '("org" "convenience" "outlines") + :url "https://github.com/jakebox/org-preview-html") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/lisp/org-preview-html/org-preview-html.el b/lisp/org-preview-html/org-preview-html.el new file mode 100644 index 00000000..4ead21ec --- /dev/null +++ b/lisp/org-preview-html/org-preview-html.el @@ -0,0 +1,238 @@ +;;; org-preview-html.el --- Automatically preview org-exported HTML files within Emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Jake B + +;; Author: Jake B +;; Original author of org-preview-html (until 2021-09): DarkSun +;; Url: https://github.com/jakebox/org-preview-html +;; Keywords: Org, convenience, outlines +;; Version: 0.3.1 +;; Package-Requires: ((emacs "25.1") (org "8.0")) + +;; This file is NOT part of GNU Emacs. + +;; 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 of the License, 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. If not, see . + +;;; Commentary: +;; This minor mode provides a side-by-side preview of your org-exported HTML +;; files using the either the eww or xwidget browsers. The update frequency of +;; the preview can be configured to suit your preference. +;; +;; Quick start: +;; Put this file under your load path. +;; Enable the minor mode in an Org buffer: +;; M-x org-preview-html-mode +;; Configure options with M-x customize-group org-preview-html +;; +;; Source code +;; org-preview-html's code can be found here: +;; http://github.com/jakebox/org-preview-html + +;;; Code: + +;;;; Requirements +(require 'org) +(require 'xwidget) +(require 'eww) + + +(defgroup org-preview-html nil + "Automatically preview org-exported HTML files within Emacs." + :group 'org-mode + :link '(url-link :tag "Homepage" "https://github.com/jakebox/org-preview-html/")) + +(defcustom org-preview-html-refresh-configuration 'save + "Specifies how often the HTML preview will be refreshed. + +If `manual', update manually by running `org-preview-html-refresh'. +If `save', update on save (default). +If `export', update on manual export \(using `org-html-export-to-html'). +If `timer', update preview on timer (`org-preview-html-timer-interval')." + :type '(choice + (symbol :tag "Update preview manually" manual) + (symbol :tag "Update preview on save" save) + (symbol :tag "Update preview on export" export) + (symbol :tag "Update preview on a timer" timer)) + :group 'org-preview-html) + +(defcustom org-preview-html-timer-interval 2 + "Integer seconds to wait between exports when in 'timer mode." + :type 'integer + :group 'org-preview-html) + +(defcustom org-preview-html-viewer 'eww + "Which Emacs browser `org-preview-html-mode' will use. + +If `eww', use eww browser (default). +If `xwidget', use xwidget browser." + :type '(choice + (symbol :tag "Use eww" eww) + (symbol :tag "Use xwidget" xwidget)) + :group 'org-preview-html) + +(define-obsolete-variable-alias 'org-preview-html/body-only 'org-preview-html-subtree-only "Version 0.3") + +(defcustom org-preview-html-subtree-only nil + "If non-nil, scope the preview to the current subtree." + :type 'boolean + :group 'org-preview-html) + +(defcustom org-preview-html/body-only nil + "Scope the preview to the body or include the entire document. +Obselete as of version 0.3, instead use `org-preview-html-subtree-only'." + :type 'boolean + :group 'org-preview-html) + + +;; Internal variables +(defvar org-preview-html--browser-buffer nil) +(defvar org-preview-html--previewed-buffer-name nil) +(defvar org-preview-html--refresh-timer nil) +(defvar-local org-preview-html--html-file nil) + + +;; https://emacs.stackexchange.com/questions/7116/pop-a-window-into-a-frame +(defun org-preview-html-pop-window-to-frame () + "Pop a window to a frame." + (interactive) + (let ((buffer (current-buffer))) + (unless (one-window-p) + (delete-window)) + (display-buffer-pop-up-frame buffer nil))) + +;; Taken from frame.el Emacs 27.1, copied here for better version compatibility. +;; Without this here 27.1 required. With, 25.1. +(defun org-preview-html--previous-window-any-frame () + (select-window (previous-window (selected-window) + (> (minibuffer-depth) 0) + 0)) + (select-frame-set-input-focus (selected-frame))) + +(defun org-preview-html-refresh () + "Exports the org file to HTML and refreshes the preview." + (interactive) + (when (eq org-preview-html-refresh-configuration 'manual) + (pop-to-buffer org-preview-html--previewed-buffer-name nil t)) + (org-preview-html--org-export-html) + (org-preview-html--reload-preview)) + +(defun org-preview-html--org-export-html () + "Silently export org to HTML." + (let ((standard-output 'ignore)) + (org-export-to-file 'html org-preview-html--html-file + nil org-preview-html-subtree-only nil nil nil nil))) + +(defun org-preview-html--reload-preview () + "Reload preview." + (save-selected-window + (pop-to-buffer org-preview-html--browser-buffer) + (cond ((eq org-preview-html-viewer 'xwidget) (xwidget-webkit-reload)) + ((eq org-preview-html-viewer 'eww) + (with-selected-window (selected-window) + ;; This stuff is to keep eww window scrolled at same point + (let ((eww-point (point)) + (eww-window-start (window-start))) + (eww-reload) + (goto-char eww-point) + (set-window-start nil eww-window-start))))))) + +(defun org-preview-html--kill-preview-buffer () + "Kill the preview buffer and pop back to the previewed org buffer." + ;; Only do these things if the preview is around + (when (bound-and-true-p org-preview-html--browser-buffer) + ;; If preview is visible we first delete the window, otherwise + ;; just kill the preview buffer + (if (get-buffer-window org-preview-html--browser-buffer 'visible) + (delete-window (get-buffer-window org-preview-html--browser-buffer))) + (let ((kill-buffer-query-functions nil)) + (kill-buffer org-preview-html--browser-buffer)) + (pop-to-buffer org-preview-html--previewed-buffer-name))) + +(defun org-preview-html--run-with-timer () + "Configure timer to refresh preview for `timer' mode." + (setq org-preview-html--refresh-timer + (run-at-time 1 org-preview-html-timer-interval #'org-preview-html-refresh))) + +(defun org-preview-html--config () + "Configure buffer for preview: add exit hooks; configure refresh hooks." + (setq org-preview-html--previewed-buffer-name (buffer-name)) + (dolist (hook '(kill-buffer-hook kill-emacs-hook)) ;; Configure exit hooks + (add-hook hook #'org-preview-html--stop-preview nil t)) + (let ((conf org-preview-html-refresh-configuration)) + (cond + ((eq conf 'manual)) + ((eq conf 'save) ;; On save + (add-hook 'after-save-hook #'org-preview-html-refresh nil t)) + ((eq conf 'timer) ;; every X seconds + (org-preview-html--run-with-timer)) + ((eq conf 'export) ;; On export using org-html-export-html command manually + (advice-add 'org-html-export-to-html :after #'org-preview-html--reload-preview))))) + +(defun org-preview-html--unconfig () + "Unconfigure 'org-preview-html-mode' (remove hooks and advice)." + (let ((conf org-preview-html-refresh-configuration)) + (cond ((eq conf 'save) + (remove-hook 'after-save-hook #'org-preview-html-refresh t)) + ((eq conf 'timer) + (cancel-timer org-preview-html--refresh-timer)) + ((eq conf 'export) + (advice-remove 'org-html-export-to-html #'org-preview-html--reload-preview)))) + (dolist (hook '(kill-buffer-hook kill-emacs-hook)) ;; Remove hooks + (remove-hook hook #'org-preview-html--stop-preview t)) + ;; Reset variables + (dolist (var '(org-preview-html--browser-buffer org-preview-html--previewed-buffer-name)) + (set var nil))) + +(defun org-preview-html--open-browser () + "Open a browser to preview the exported HTML file." + ;; Store the exported HTML filename + (setq-local org-preview-html--html-file (concat (file-name-sans-extension buffer-file-name) ".html")) + (org-preview-html--org-export-html) ;; Export the org file to HTML + ;; Procedure to open the side-by-side preview + (split-window-right) + (other-window 1) + (let ((file org-preview-html--html-file)) + (cond ((eq org-preview-html-viewer 'xwidget) (xwidget-webkit-browse-url (concat "file://" file))) + ((eq org-preview-html-viewer 'eww) (eww-open-file file)))) + (setq org-preview-html--browser-buffer (get-buffer (buffer-name))) + (org-preview-html--previous-window-any-frame)) + +(defun org-preview-html--start-preview () + "Begin the org-preview-html preview." + (when buffer-file-name + (cond ((derived-mode-p 'org-mode) + (message "org-preview-html has recieved a major update - xwidgets support, refresh configurations and more! \n M-x customize-group org-preview-html-mode") + (org-preview-html--open-browser) + (org-preview-html--config)) + (t + (org-preview-html-mode -1) + (user-error "`%s' not supported by org-preview-html preview, only `org mode'!" major-mode))))) + +(defun org-preview-html--stop-preview () + "Stop the org-preview-html preview." + (org-preview-html--kill-preview-buffer) + (org-preview-html--unconfig)) + + +;;;###autoload +(define-minor-mode org-preview-html-mode + "(Optionally) live preview for Org exports to HTML." + :lighter " org-preview-html" + (if org-preview-html-mode + (org-preview-html--start-preview) + (org-preview-html--stop-preview))) + +(provide 'org-preview-html) + +;;; org-preview-html.el ends here