-
Notifications
You must be signed in to change notification settings - Fork 14
/
org-preview-html.el
238 lines (202 loc) · 9.18 KB
/
org-preview-html.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
;;; org-preview-html.el --- Automatically preview org-exported HTML files within Emacs -*- lexical-binding: t; -*-
;; Copyright (C) 2021 Jake B <[email protected]>
;; Author: Jake B <[email protected]>
;; Original author of org-preview-html (until 2021-09): DarkSun <[email protected]>
;; 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 <https://www.gnu.org/licenses/>.
;;; 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