fronde 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fronde/config/data/org-config.el +1 -1
- data/lib/fronde/config/data/ox-fronde.el +1 -1
- data/lib/fronde/config/data/ox-gmi.el +654 -0
- data/lib/fronde/config/helpers.rb +1 -1
- data/lib/fronde/config/lisp.rb +14 -13
- data/lib/fronde/index/atom_generator.rb +21 -19
- data/lib/fronde/index/data/template.xml +9 -4
- data/lib/fronde/org/file.rb +1 -1
- data/lib/fronde/org.rb +20 -7
- data/lib/fronde/version.rb +5 -1
- data/lib/tasks/org.rake +12 -17
- metadata +15 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f351a91fd7e7e674a9e7b3120725960cd36cc02a4c7d6d39715f760f4527b75
|
|
4
|
+
data.tar.gz: 4b371b41a1658aed730c2cb533bc8f68178d3a0d1ae791fb598aa7ab9eda5372
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb1c612a0e6139a16ef798847579018494a171965e05b4535621906c4c488f62796fbbaa234b2b6566596335c47b9bce49d32d47eda772941ae8829e3ee2443f
|
|
7
|
+
data.tar.gz: 10b549865a13777b2e37e8beff967603b030d51bb7e3d7ea1bd664e2883d4b9c5c13271c1c6c718d8d6f6b8e70064ed5e032e81e7a84aecb70d3fc95e7480685
|
|
@@ -21,5 +21,5 @@
|
|
|
21
21
|
("website" :components ("{{ all_projects | map: 'name' | join: '" "' | remove: '" "tags' }}"))))
|
|
22
22
|
|
|
23
23
|
;; Load fronde lib
|
|
24
|
-
(load-file (expand-file-name "ox-gmi.el" "{{
|
|
24
|
+
(load-file (expand-file-name "ox-gmi.el" "{{ fronde_data_dir }}"))
|
|
25
25
|
(load-file (expand-file-name "ox-fronde.el" "{{ fronde_data_dir }}"))
|
|
@@ -97,7 +97,7 @@ INFO is a plist used as a communication channel."
|
|
|
97
97
|
output)
|
|
98
98
|
(push `(?l . ,(org-export-data (plist-get info :language) info)) output)
|
|
99
99
|
(push `(?n . ,(format "Fronde %s" fronde-version)) output)
|
|
100
|
-
(push `(?N . ,(format "<a href=\"https://etienne.
|
|
100
|
+
(push `(?N . ,(format "<a href=\"https://etienne.pflieger.bzh/fronde/\">Fronde</a> %s" fronde-version)) output)
|
|
101
101
|
(push `(?x . ,(org-export-data (plist-get info :description) info)) output)
|
|
102
102
|
(push `(?X . ,(format "<p>%s</p>"
|
|
103
103
|
(org-export-data (plist-get info :description) info)))
|
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
;;; ox-gmi.el --- Gemini Back-End for Org Export Engine -*- lexical-binding: t; -*-
|
|
2
|
+
|
|
3
|
+
;; Copyright (C) 2020 Étienne Pflieger
|
|
4
|
+
|
|
5
|
+
;; Author: Étienne Pflieger <etienne@pflieger.bzh>
|
|
6
|
+
;; Created: 29 November 2020
|
|
7
|
+
;; Version: 0.2
|
|
8
|
+
;; Package-Requires: ((emacs "27.1"))
|
|
9
|
+
;; Keywords: wp
|
|
10
|
+
;; Homepage: https://git.umaneti.net/ox-gmi.el/
|
|
11
|
+
|
|
12
|
+
;;; License:
|
|
13
|
+
|
|
14
|
+
;; This file is not part of GNU Emacs.
|
|
15
|
+
;; However, it is distributed under the same license.
|
|
16
|
+
|
|
17
|
+
;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
18
|
+
;; it under the terms of the GNU General Public License as published by
|
|
19
|
+
;; the Free Software Foundation; either version 3, or (at your option)
|
|
20
|
+
;; any later version.
|
|
21
|
+
|
|
22
|
+
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
23
|
+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
24
|
+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
25
|
+
;; GNU General Public License for more details.
|
|
26
|
+
|
|
27
|
+
;; You should have received a copy of the GNU General Public License
|
|
28
|
+
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
|
29
|
+
|
|
30
|
+
;;; Commentary:
|
|
31
|
+
|
|
32
|
+
;; This library implements a Gemini back-end for Org exporter, based on
|
|
33
|
+
;; `markdown' back-end. It also heavily depends on the `ascii' back-end.
|
|
34
|
+
|
|
35
|
+
;;; Installation:
|
|
36
|
+
|
|
37
|
+
;; Obviously, this package depends on `org-mode', which must be loaded first.
|
|
38
|
+
|
|
39
|
+
;; You first need to download this file. We recommend you to put it
|
|
40
|
+
;; somewhere under your `user-emacs-directory' folder (often ~/.emacs.d).
|
|
41
|
+
|
|
42
|
+
;; cd ~/.emacs.d
|
|
43
|
+
;; curl -O https://git.umaneti.net/fronde/plain/lib/fronde/config/data/ox-gmi.el
|
|
44
|
+
|
|
45
|
+
;; Then, you may want to compile it. You can do so with the Emacs command
|
|
46
|
+
;; M-x byte-compile-file RET ~/.emacs.d/ox-gmi.el
|
|
47
|
+
|
|
48
|
+
;; When it's done, you can add the following line in your GNU Emacs config
|
|
49
|
+
;; file:
|
|
50
|
+
;; (load-file "~/.emacs.d/ox-gmi.elc")
|
|
51
|
+
|
|
52
|
+
;; And everything should be fine.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
;;; Code:
|
|
56
|
+
|
|
57
|
+
(require 'ox-ascii)
|
|
58
|
+
(require 'ox-publish)
|
|
59
|
+
|
|
60
|
+
(declare-function gemini-mode "gemini")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
;;; Define Back-End
|
|
64
|
+
|
|
65
|
+
(org-export-define-derived-backend 'gmi 'ascii
|
|
66
|
+
:menu-entry
|
|
67
|
+
'(?g "Export to Gemini"
|
|
68
|
+
((?G "To temporary buffer"
|
|
69
|
+
(lambda (a s v b) (org-gmi-export-as-gemini a s v)))
|
|
70
|
+
(?g "To file"
|
|
71
|
+
(lambda (a s v b) (org-gmi-export-to-gemini a s v)))
|
|
72
|
+
(?o "To file and open"
|
|
73
|
+
(lambda (a s v b)
|
|
74
|
+
(if a (org-gmi-export-to-gemini t s v)
|
|
75
|
+
(org-open-file (org-gmi-export-to-gemini nil s v)))))))
|
|
76
|
+
:translate-alist '((center-block . org-gmi-center)
|
|
77
|
+
(example-block . org-gmi-preformatted-block)
|
|
78
|
+
(export-block . org-gmi-export-block)
|
|
79
|
+
(fixed-width . org-gmi-preformatted-block)
|
|
80
|
+
(footnote-reference . org-gmi-footnote-reference)
|
|
81
|
+
(headline . org-gmi-headline)
|
|
82
|
+
(inner-template . org-gmi-inner-template)
|
|
83
|
+
(item . org-gmi-item)
|
|
84
|
+
(keyword . org-gmi-keyword)
|
|
85
|
+
(line-break . org-gmi-line-break)
|
|
86
|
+
(link . org-gmi-link)
|
|
87
|
+
(paragraph . org-gmi-paragraph)
|
|
88
|
+
(quote-block . org-gmi-quote-block)
|
|
89
|
+
(section . org-gmi-section)
|
|
90
|
+
(src-block . org-gmi-preformatted-block)
|
|
91
|
+
(template . org-gmi-template))
|
|
92
|
+
:options-alist '((:creator "CREATOR" nil org-gmi-creator-string)
|
|
93
|
+
(:description "DESCRIPTION" nil nil parse)
|
|
94
|
+
(:keywords "KEYWORDS" nil nil parse)
|
|
95
|
+
(:subtitle "SUBTITLE" nil nil parse)
|
|
96
|
+
(:gemini-postamble
|
|
97
|
+
nil "gemini-postamble" org-gmi-postamble)
|
|
98
|
+
(:gemini-timestamp-format
|
|
99
|
+
nil nil org-gmi-timestamp-format)
|
|
100
|
+
(:gemini-postamble-format
|
|
101
|
+
nil nil org-gmi-postamble-format)))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
;;; Inner Variables
|
|
106
|
+
|
|
107
|
+
(defvar org-gmi--links-in-section '()
|
|
108
|
+
"AList storing all links in current section.")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
;;; User Configuration Variables
|
|
113
|
+
|
|
114
|
+
(defgroup org-export-gmi nil
|
|
115
|
+
"Options for exporting Org mode files to Gemini."
|
|
116
|
+
:tag "Org Export Gemini"
|
|
117
|
+
:group 'org-export)
|
|
118
|
+
|
|
119
|
+
(defcustom org-gmi-postamble 't
|
|
120
|
+
"Non-nil means insert a postamble in Gemini export.
|
|
121
|
+
|
|
122
|
+
When set to `auto', check against the
|
|
123
|
+
`org-export-with-author/email/creator/date' variables to set the
|
|
124
|
+
content of the postamble. When set to a string, use this string
|
|
125
|
+
as the postamble. When t, insert a string as defined by the
|
|
126
|
+
formatting string in `org-gmi-postamble-format'.
|
|
127
|
+
|
|
128
|
+
When set to a function, apply this function and insert the
|
|
129
|
+
returned string. The function takes the property list of export
|
|
130
|
+
options as its only argument.
|
|
131
|
+
|
|
132
|
+
Setting :gemini-postamble in publishing projects will take
|
|
133
|
+
precedence over this variable."
|
|
134
|
+
:group 'org-export-gmi
|
|
135
|
+
:type '(choice (const :tag "No postamble" nil)
|
|
136
|
+
(const :tag "Auto postamble" auto)
|
|
137
|
+
(const :tag "Default formatting string" t)
|
|
138
|
+
(string :tag "Custom formatting string")
|
|
139
|
+
(function :tag "Function (must return a string)")))
|
|
140
|
+
|
|
141
|
+
(defcustom org-gmi-postamble-format
|
|
142
|
+
'(("en" "📝 %a with %c\n📅 %d")
|
|
143
|
+
("fr" "📝 %a avec %c\n📅 %d"))
|
|
144
|
+
"Alist of languages and format strings for the Gemini postamble.
|
|
145
|
+
|
|
146
|
+
The first element of each list is the language code, as used for
|
|
147
|
+
the LANGUAGE keyword. See `org-export-default-language'.
|
|
148
|
+
|
|
149
|
+
The second element of each list is a format string to format the
|
|
150
|
+
postamble itself. This format string can contain these elements:
|
|
151
|
+
|
|
152
|
+
%t stands for the title.
|
|
153
|
+
%s stands for the subtitle.
|
|
154
|
+
%a stands for the author's name.
|
|
155
|
+
%e stands for the author's email.
|
|
156
|
+
%d stands for the date.
|
|
157
|
+
%k stands for the keywords list.
|
|
158
|
+
%l stands for the language code name.
|
|
159
|
+
%x stands for the description.
|
|
160
|
+
%c will be replaced by `org-gmi-creator-string'.
|
|
161
|
+
%T will be replaced by the export time.
|
|
162
|
+
%C will be replaced by the last modification time.
|
|
163
|
+
|
|
164
|
+
If you need to use a \"%\" character, you need to escape it
|
|
165
|
+
like that: \"%%\"."
|
|
166
|
+
:group 'org-export-gmi
|
|
167
|
+
:type '(repeat
|
|
168
|
+
(list (string :tag "Language")
|
|
169
|
+
(string :tag "Format string"))))
|
|
170
|
+
|
|
171
|
+
(defcustom org-gmi-creator-string
|
|
172
|
+
(format "GNU/Emacs %s (Org mode %s)"
|
|
173
|
+
emacs-version
|
|
174
|
+
(if (fboundp 'org-version) (org-version) "unknown version"))
|
|
175
|
+
"Information about the creator of the Gemini document.
|
|
176
|
+
This option can also be set on with the CREATOR keyword."
|
|
177
|
+
:group 'org-export-gmi
|
|
178
|
+
:type '(string :tag "Creator string"))
|
|
179
|
+
|
|
180
|
+
(defcustom org-gmi-timestamp-format "%Y-%m-%d %a %H:%M"
|
|
181
|
+
"Format used for timestamps in postamble.
|
|
182
|
+
See `format-time-string' for more information on its components."
|
|
183
|
+
:group 'org-export-gmi
|
|
184
|
+
:type 'string)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
;;; Inner Functions
|
|
189
|
+
|
|
190
|
+
(defun org-gmi--build-headline (level title &optional tags)
|
|
191
|
+
"Generate a headline TITLE for the given LEVEL.
|
|
192
|
+
TAGS are the tags set on the section."
|
|
193
|
+
(let ((level-mark (make-string level ?#)))
|
|
194
|
+
(concat level-mark " " title tags "\n\n")))
|
|
195
|
+
|
|
196
|
+
(defun org-gmi--build-links-list (links)
|
|
197
|
+
"Return a string describing a list of links.
|
|
198
|
+
LINKS is an alist like `org-gmi--links-in-section'"
|
|
199
|
+
(let (links-list)
|
|
200
|
+
(string-trim-right
|
|
201
|
+
(apply #'concat
|
|
202
|
+
(reverse
|
|
203
|
+
(dolist (link-data links links-list)
|
|
204
|
+
(push (apply #'org-gmi--format-link link-data)
|
|
205
|
+
links-list)))))))
|
|
206
|
+
|
|
207
|
+
(defun org-gmi--build-toc (info &optional depth)
|
|
208
|
+
"Return a table of contents.
|
|
209
|
+
INFO is a plist used as a communication channel.
|
|
210
|
+
Optional argument DEPTH, when non-nil, is an integer specifying the
|
|
211
|
+
depth of the table."
|
|
212
|
+
(mapconcat
|
|
213
|
+
(lambda (headline)
|
|
214
|
+
(let* ((prefix
|
|
215
|
+
(if (not (org-export-numbered-headline-p headline info)) ""
|
|
216
|
+
(concat
|
|
217
|
+
(mapconcat 'number-to-string
|
|
218
|
+
(org-export-get-headline-number headline info)
|
|
219
|
+
".")
|
|
220
|
+
". ")))
|
|
221
|
+
(title (org-export-data-with-backend
|
|
222
|
+
(org-export-get-alt-title headline info)
|
|
223
|
+
(org-export-toc-entry-backend 'gmi)
|
|
224
|
+
info))
|
|
225
|
+
(tags (and (plist-get info :with-tags)
|
|
226
|
+
(not (eq 'not-in-toc (plist-get info :with-tags)))
|
|
227
|
+
(org-make-tag-string
|
|
228
|
+
(org-export-get-tags headline info)))))
|
|
229
|
+
(concat prefix title tags)))
|
|
230
|
+
(org-export-collect-headlines info depth) "\n"))
|
|
231
|
+
|
|
232
|
+
(defun org-gmi--build-postamble (info)
|
|
233
|
+
"Return document postamble as a string, or nil.
|
|
234
|
+
INFO is a plist used as a communication channel."
|
|
235
|
+
(let ((section (plist-get info :gemini-postamble))
|
|
236
|
+
(spec (org-gmi--format-spec info)))
|
|
237
|
+
(when section
|
|
238
|
+
(let ((section-contents
|
|
239
|
+
(if (functionp section) (funcall section info)
|
|
240
|
+
(cond
|
|
241
|
+
((stringp section) (format-spec section spec))
|
|
242
|
+
((eq section 'auto)
|
|
243
|
+
(let ((date (cdr (assq ?d spec)))
|
|
244
|
+
(author (cdr (assq ?a spec)))
|
|
245
|
+
(email (cdr (assq ?e spec)))
|
|
246
|
+
(creator (cdr (assq ?c spec))))
|
|
247
|
+
(concat
|
|
248
|
+
(and (plist-get info :with-date)
|
|
249
|
+
(org-string-nw-p date)
|
|
250
|
+
(format "📅 %s\n" date))
|
|
251
|
+
(and (plist-get info :with-author)
|
|
252
|
+
(org-string-nw-p author)
|
|
253
|
+
(format "📝 %s\n" author))
|
|
254
|
+
(and (plist-get info :with-email)
|
|
255
|
+
(org-string-nw-p email)
|
|
256
|
+
(format "💌 %s\n" email))
|
|
257
|
+
(and (plist-get info :time-stamp-file)
|
|
258
|
+
(format
|
|
259
|
+
"🕓 %s\n"
|
|
260
|
+
(format-time-string
|
|
261
|
+
(plist-get info :gemini-timestamp-format))))
|
|
262
|
+
(and (plist-get info :with-creator)
|
|
263
|
+
(org-string-nw-p creator)
|
|
264
|
+
(format "⚙ %s\n" creator)))))
|
|
265
|
+
(t
|
|
266
|
+
(let ((formats (plist-get info :gemini-postamble-format))
|
|
267
|
+
(language (plist-get info :language)))
|
|
268
|
+
(format-spec
|
|
269
|
+
(cadr (or (assoc-string language formats t)
|
|
270
|
+
(assoc-string "en" formats t)))
|
|
271
|
+
spec)))))))
|
|
272
|
+
(when (org-string-nw-p section-contents)
|
|
273
|
+
(format "\n\n--\n%s\n"
|
|
274
|
+
(org-element-normalize-string section-contents)))))))
|
|
275
|
+
|
|
276
|
+
(defun org-gmi--format-spec (info)
|
|
277
|
+
"Return format specification for postamble.
|
|
278
|
+
INFO is a plist used as a communication channel."
|
|
279
|
+
(let ((timestamp-format (plist-get info :gemini-timestamp-format)))
|
|
280
|
+
`((?t . ,(org-export-data (plist-get info :title) info))
|
|
281
|
+
(?s . ,(org-export-data (plist-get info :subtitle) info))
|
|
282
|
+
(?d . ,(org-export-data (org-export-get-date info timestamp-format) info))
|
|
283
|
+
(?T . ,(format-time-string timestamp-format))
|
|
284
|
+
(?a . ,(org-export-data (plist-get info :author) info))
|
|
285
|
+
(?e . ,(org-export-data (plist-get info :email) info))
|
|
286
|
+
(?c . ,(plist-get info :creator))
|
|
287
|
+
(?C . ,(let ((file (plist-get info :input-file)))
|
|
288
|
+
(format-time-string
|
|
289
|
+
timestamp-format
|
|
290
|
+
(and file (file-attribute-modification-time
|
|
291
|
+
(file-attributes file))))))
|
|
292
|
+
(?k . ,(org-export-data (plist-get info :keywords) info))
|
|
293
|
+
(?l . ,(org-export-data (plist-get info :language) info))
|
|
294
|
+
(?x . ,(org-export-data (plist-get info :description) info)))))
|
|
295
|
+
|
|
296
|
+
(defun org-gmi--format-paragraph (paragraph &optional prefix)
|
|
297
|
+
"Transcode PARAGRAPH into Gemini format.
|
|
298
|
+
If PREFIX is non-nil, add it at the beginning of each lines."
|
|
299
|
+
(replace-regexp-in-string
|
|
300
|
+
"^\\s-?" (or prefix "")
|
|
301
|
+
(org-trim
|
|
302
|
+
(replace-regexp-in-string "\r?\n\\([^\r\n]\\)" " \\1" paragraph))))
|
|
303
|
+
|
|
304
|
+
(defun org-gmi--format-link (dest label &optional reference)
|
|
305
|
+
"Return a formatted link pointing to DEST with the given LABEL.
|
|
306
|
+
|
|
307
|
+
When REFERENCE is given, it is added between bracket to generate single link
|
|
308
|
+
entry in a links list."
|
|
309
|
+
(if reference
|
|
310
|
+
(format "=> %s [%d] %s\n" dest reference label)
|
|
311
|
+
(format "=> %s %s\n" dest label)))
|
|
312
|
+
|
|
313
|
+
(defun org-gmi--link-alone-on-line-p (link)
|
|
314
|
+
"Return t if the given LINK occupies its whole line."
|
|
315
|
+
(let* ((link-start (org-element-property :begin link))
|
|
316
|
+
(link-end (org-element-property :end link))
|
|
317
|
+
(full-link (buffer-substring-no-properties link-start link-end))
|
|
318
|
+
(raw-link (org-element-property :raw-link link)))
|
|
319
|
+
(save-excursion
|
|
320
|
+
(org-goto-line (org-current-line link-start))
|
|
321
|
+
(let ((line-content (org-trim (thing-at-point 'line t))))
|
|
322
|
+
(or (string= raw-link line-content)
|
|
323
|
+
(string= full-link line-content))))))
|
|
324
|
+
|
|
325
|
+
(defun org-gmi--extract-picture-label (link info)
|
|
326
|
+
"Extract a worthy label for the given LINK, when HREF points to a picture."
|
|
327
|
+
(let* ((paragraph (org-element-parent link))
|
|
328
|
+
(alt (plist-get
|
|
329
|
+
(org-export-read-attribute :attr_gmi paragraph)
|
|
330
|
+
:alt))
|
|
331
|
+
(caption (org-export-data (org-export-get-caption paragraph) info)))
|
|
332
|
+
(if (org-string-nw-p caption)
|
|
333
|
+
caption
|
|
334
|
+
alt)))
|
|
335
|
+
|
|
336
|
+
(defun org-gmi--number-to-utf8-exponent (number)
|
|
337
|
+
"Convert a NUMBER to its utf8 exponent display."
|
|
338
|
+
(let ((exponents '((?1 . "¹")
|
|
339
|
+
(?2 . "²")
|
|
340
|
+
(?3 . "³")
|
|
341
|
+
(?4 . "⁴")
|
|
342
|
+
(?5 . "⁵")
|
|
343
|
+
(?6 . "⁶")
|
|
344
|
+
(?7 . "⁷")
|
|
345
|
+
(?8 . "⁸")
|
|
346
|
+
(?9 . "⁹")
|
|
347
|
+
(?0 . "⁰"))))
|
|
348
|
+
(mapconcat
|
|
349
|
+
(lambda (digit) (cdr (assoc digit exponents)))
|
|
350
|
+
(number-to-string number)
|
|
351
|
+
"")))
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
;;; Transcode Functions
|
|
355
|
+
|
|
356
|
+
(defun org-gmi-preformatted-block (block _contents info)
|
|
357
|
+
"Transcode BLOCK element into Gemini format.
|
|
358
|
+
INFO is a plist used as a communication channel."
|
|
359
|
+
(let ((language (org-export-data (org-element-property :language block) info))
|
|
360
|
+
(caption (org-export-data (org-export-get-caption block) info)))
|
|
361
|
+
(setq caption (if (org-string-nw-p caption)
|
|
362
|
+
(format "%s - %s" language caption)
|
|
363
|
+
language))
|
|
364
|
+
(format "```%s\n%s```\n"
|
|
365
|
+
caption
|
|
366
|
+
(org-remove-indentation
|
|
367
|
+
(org-export-format-code-default block info)))))
|
|
368
|
+
|
|
369
|
+
(defun org-gmi-export-block (export-block _contents _info)
|
|
370
|
+
"Transcode a EXPORT-BLOCK element from Org to Gemini."
|
|
371
|
+
(if (member (org-element-property :type export-block) '("GEMINI" "GMI"))
|
|
372
|
+
(org-remove-indentation (org-element-property :value export-block))))
|
|
373
|
+
|
|
374
|
+
(defun org-gmi-center (_center contents info)
|
|
375
|
+
"Transcode a CENTER block from Org to Gemini.
|
|
376
|
+
CONTENTS is the block value. INFO is a plist holding contextual
|
|
377
|
+
information."
|
|
378
|
+
(format "```\n%s```"
|
|
379
|
+
(org-ascii--fill-string contents 80 info 'center)))
|
|
380
|
+
|
|
381
|
+
(defun org-gmi-entity (_entity contents _info)
|
|
382
|
+
"Transcode an ENTITY object from Org to Gemini.
|
|
383
|
+
CONTENTS is the entity itself."
|
|
384
|
+
contents)
|
|
385
|
+
|
|
386
|
+
(defun org-gmi-footnote-reference (footnote-reference _contents info)
|
|
387
|
+
"Transcode a FOOTNOTE-REFERENCE element from Org to Gemini.
|
|
388
|
+
CONTENTS is nil. INFO is a plist holding contextual information."
|
|
389
|
+
(org-gmi--number-to-utf8-exponent
|
|
390
|
+
(org-export-get-footnote-number footnote-reference info)))
|
|
391
|
+
|
|
392
|
+
(defun org-gmi-headline (headline contents info)
|
|
393
|
+
"Transcode HEADLINE element into Gemini format.
|
|
394
|
+
CONTENTS is the headline value. INFO is a plist used as
|
|
395
|
+
a communication channel."
|
|
396
|
+
(let* ((level (1+ (org-export-get-relative-level headline info)))
|
|
397
|
+
(title (org-export-data (org-element-property :title headline) info))
|
|
398
|
+
(todo (and (plist-get info :with-todo-keywords)
|
|
399
|
+
(let ((todo (org-element-property :todo-keyword
|
|
400
|
+
headline)))
|
|
401
|
+
(and todo (concat (org-export-data todo info) " ")))))
|
|
402
|
+
(tags (and (plist-get info :with-tags)
|
|
403
|
+
(let ((tag-list (org-export-get-tags headline info)))
|
|
404
|
+
(and tag-list
|
|
405
|
+
(concat " " (org-make-tag-string tag-list))))))
|
|
406
|
+
(priority
|
|
407
|
+
(and (plist-get info :with-priority)
|
|
408
|
+
(let ((char (org-element-property :priority headline)))
|
|
409
|
+
(and char (format "[#%c] " char)))))
|
|
410
|
+
;; Headline text without tags.
|
|
411
|
+
(heading (concat todo priority title)))
|
|
412
|
+
(if
|
|
413
|
+
;; Cannot create a headline. Fall-back to a list.
|
|
414
|
+
(or (org-export-low-level-p headline info)
|
|
415
|
+
(> level 3)) ;; Gemtext only allow 3 levels of title
|
|
416
|
+
(let ((bullet
|
|
417
|
+
(if (not (org-export-numbered-headline-p headline info)) "*"
|
|
418
|
+
(concat (number-to-string
|
|
419
|
+
(car (last (org-export-get-headline-number
|
|
420
|
+
headline info))))
|
|
421
|
+
"."))))
|
|
422
|
+
(concat bullet (make-string (- 4 (length bullet)) ?\s)
|
|
423
|
+
heading tags "\n\n" contents))
|
|
424
|
+
;; Else
|
|
425
|
+
(concat (org-gmi--build-headline level heading tags) contents))))
|
|
426
|
+
|
|
427
|
+
(defun org-gmi-inner-template (contents info)
|
|
428
|
+
"Return body of document after converting it to Gemini syntax.
|
|
429
|
+
CONTENTS is the transcoded contents string. INFO is a plist
|
|
430
|
+
holding export options."
|
|
431
|
+
(concat
|
|
432
|
+
;; Document contents.
|
|
433
|
+
contents
|
|
434
|
+
;; Footnotes section.
|
|
435
|
+
(let ((definitions (org-export-collect-footnote-definitions info)))
|
|
436
|
+
(when definitions
|
|
437
|
+
(concat
|
|
438
|
+
"\n\n"
|
|
439
|
+
(org-gmi--build-headline
|
|
440
|
+
2 (org-ascii--translate "Footnotes" info))
|
|
441
|
+
(mapconcat
|
|
442
|
+
(lambda (ref)
|
|
443
|
+
(let ((id (car ref))
|
|
444
|
+
(def (nth 2 ref)))
|
|
445
|
+
(format "%s %s" (org-gmi--number-to-utf8-exponent id)
|
|
446
|
+
(replace-regexp-in-string
|
|
447
|
+
"\r?\n" " "
|
|
448
|
+
(org-trim (org-export-data def info))))))
|
|
449
|
+
definitions "\n\n"))))))
|
|
450
|
+
|
|
451
|
+
(defun org-gmi-template (contents info)
|
|
452
|
+
"Return body of document after converting it to Gemini syntax.
|
|
453
|
+
CONTENTS is the transcoded contents string. INFO is a plist
|
|
454
|
+
holding export options."
|
|
455
|
+
(concat
|
|
456
|
+
;; Document title.
|
|
457
|
+
(org-gmi--build-headline
|
|
458
|
+
1 (org-export-data (plist-get info :title) info))
|
|
459
|
+
;; TOC
|
|
460
|
+
(let* ((depth (plist-get info :with-toc))
|
|
461
|
+
(toc-contents (when depth
|
|
462
|
+
(org-gmi--build-toc
|
|
463
|
+
info (and (wholenump depth) depth)))))
|
|
464
|
+
(when (org-string-nw-p toc-contents)
|
|
465
|
+
(concat
|
|
466
|
+
(org-gmi--build-headline 2 (org-ascii--translate "Table of Contents" info))
|
|
467
|
+
toc-contents
|
|
468
|
+
"\n\n\n")))
|
|
469
|
+
;; Document contents.
|
|
470
|
+
contents
|
|
471
|
+
;; Postamble
|
|
472
|
+
(org-gmi--build-postamble info)))
|
|
473
|
+
|
|
474
|
+
(defun org-gmi-item (item contents info)
|
|
475
|
+
"Transcode ITEM element into Gemini format.
|
|
476
|
+
CONTENTS is the item value. INFO is a plist used as a
|
|
477
|
+
communication channel."
|
|
478
|
+
(let* ((type (org-element-property :type (org-export-get-parent item)))
|
|
479
|
+
(struct (org-element-property :structure item))
|
|
480
|
+
(bullet (if (not (eq type 'ordered)) "*"
|
|
481
|
+
(concat (number-to-string
|
|
482
|
+
(car (last (org-list-get-item-number
|
|
483
|
+
(org-element-property :begin item)
|
|
484
|
+
struct
|
|
485
|
+
(org-list-prevs-alist struct)
|
|
486
|
+
(org-list-parents-alist struct)))))
|
|
487
|
+
"."))))
|
|
488
|
+
(concat bullet
|
|
489
|
+
" "
|
|
490
|
+
(pcase (org-element-property :checkbox item)
|
|
491
|
+
(`on "[X] ")
|
|
492
|
+
(`trans "[-] ")
|
|
493
|
+
(`off "[ ] "))
|
|
494
|
+
(let ((tag (org-element-property :tag item)))
|
|
495
|
+
(and tag (format "%s: " (org-export-data tag info))))
|
|
496
|
+
(and contents (org-trim contents)))))
|
|
497
|
+
|
|
498
|
+
(defun org-gmi-keyword (keyword _contents _info)
|
|
499
|
+
"Transcode a KEYWORD element into Gemini format.
|
|
500
|
+
CONTENTS is nil. INFO is a plist used as a communication
|
|
501
|
+
channel."
|
|
502
|
+
(when (member (org-element-property :key keyword) '("GEMINI" "GMI"))
|
|
503
|
+
(org-element-property :value keyword)))
|
|
504
|
+
|
|
505
|
+
(defun org-gmi-line-break (_line-break _contents _info)
|
|
506
|
+
"Transcode LINE-BREAK object into Gemini format.
|
|
507
|
+
CONTENTS is nil. INFO is a plist used as a communication
|
|
508
|
+
channel."
|
|
509
|
+
" ")
|
|
510
|
+
|
|
511
|
+
(defun org-gmi-link (link desc info)
|
|
512
|
+
"Transcode a LINK object from Org to Gemini.
|
|
513
|
+
DESC is the description part of the link, or the empty string."
|
|
514
|
+
(let ((link-type (org-element-property :type link))
|
|
515
|
+
(link-path (org-element-property :path link))
|
|
516
|
+
href lang)
|
|
517
|
+
;; Cleanup path
|
|
518
|
+
(setq href
|
|
519
|
+
(if (member link-type '("file" "fuzzy"))
|
|
520
|
+
(if (string= ".org" (downcase (file-name-extension link-path ".")))
|
|
521
|
+
;; Replace local org file to gmi files during publication
|
|
522
|
+
(format "%s.gmi" (file-name-sans-extension link-path))
|
|
523
|
+
link-path)
|
|
524
|
+
(org-element-property :raw-link link)))
|
|
525
|
+
(when (string= (substring href 0 5) "i18n:")
|
|
526
|
+
(pcase-let ((`(,path ,raw-lang) (split-string (substring href 5) "::")))
|
|
527
|
+
(setq href path lang raw-lang)))
|
|
528
|
+
(let* ((scheme (car (split-string href ":" t)))
|
|
529
|
+
;; Avoid cut lines in link labels
|
|
530
|
+
(raw-label (replace-regexp-in-string "\r?\n" " " (or desc href)))
|
|
531
|
+
(label (if (and (not (member href (list desc scheme))) ;; relative link
|
|
532
|
+
(not (member (downcase scheme)
|
|
533
|
+
'("gemini" "file"))))
|
|
534
|
+
(format "%s (%s)" raw-label (upcase scheme))
|
|
535
|
+
raw-label)))
|
|
536
|
+
(when lang (setq label (format "%s [%s]" label lang)))
|
|
537
|
+
;; Handle pictures
|
|
538
|
+
(let* ((dest-ext (file-name-extension href))
|
|
539
|
+
(found-label
|
|
540
|
+
(when (and (not desc)
|
|
541
|
+
(member (or (and dest-ext (downcase dest-ext)) "")
|
|
542
|
+
'("jpeg" "jpg" "png" "gif" "svg" "webp" "avif")))
|
|
543
|
+
(org-gmi--extract-picture-label link info))))
|
|
544
|
+
(when found-label (setq label found-label)))
|
|
545
|
+
;; Do we need to add the link at the end of the section or should it be
|
|
546
|
+
;; directly printed in its own line?
|
|
547
|
+
(if (org-gmi--link-alone-on-line-p link)
|
|
548
|
+
(org-gmi--format-link href label)
|
|
549
|
+
;; As links are specific for a section, which should not be that long (?),
|
|
550
|
+
;; we will always use the first label encountered for a link as
|
|
551
|
+
;; reference.
|
|
552
|
+
(let ((link-data (assoc href org-gmi--links-in-section)))
|
|
553
|
+
(unless link-data
|
|
554
|
+
(setq link-data (list href label
|
|
555
|
+
;; Default next-reference
|
|
556
|
+
(1+ (length org-gmi--links-in-section))))
|
|
557
|
+
(add-to-list 'org-gmi--links-in-section link-data t))
|
|
558
|
+
(format "%s[%d]" raw-label (caddr link-data)))))))
|
|
559
|
+
|
|
560
|
+
(defun org-gmi-paragraph (_paragraph contents _info)
|
|
561
|
+
"Transcode PARAGRAPH element into Gemini format.
|
|
562
|
+
CONTENTS is the paragraph value."
|
|
563
|
+
(org-gmi--format-paragraph contents))
|
|
564
|
+
|
|
565
|
+
(defun org-gmi-quote-block (_quote-block contents _info)
|
|
566
|
+
"Transcode QUOTE-BLOCK element into Gemini format.
|
|
567
|
+
CONTENTS is the quote-block value."
|
|
568
|
+
(org-gmi--format-paragraph contents "> "))
|
|
569
|
+
|
|
570
|
+
(defun org-gmi-section (_section contents _info)
|
|
571
|
+
"Transcode SECTION into Gemini format.
|
|
572
|
+
CONTENTS is the section value."
|
|
573
|
+
(let ((output
|
|
574
|
+
(format "%s\n%s"
|
|
575
|
+
contents
|
|
576
|
+
(org-gmi--build-links-list org-gmi--links-in-section))))
|
|
577
|
+
(setq org-gmi--links-in-section '()) ;; Reset link list
|
|
578
|
+
output))
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
;;; Interactive function
|
|
582
|
+
|
|
583
|
+
;;;###autoload
|
|
584
|
+
(defun org-gmi-export-as-gemini (&optional async subtreep visible-only)
|
|
585
|
+
"Export current buffer to a Gemini buffer.
|
|
586
|
+
|
|
587
|
+
If narrowing is active in the current buffer, only export its
|
|
588
|
+
narrowed part.
|
|
589
|
+
|
|
590
|
+
If a region is active, export that region.
|
|
591
|
+
|
|
592
|
+
A non-nil optional argument ASYNC means the process should happen
|
|
593
|
+
asynchronously. The resulting buffer should be accessible
|
|
594
|
+
through the `org-export-stack' interface.
|
|
595
|
+
|
|
596
|
+
When optional argument SUBTREEP is non-nil, export the sub-tree
|
|
597
|
+
at point, extracting information from the headline properties
|
|
598
|
+
first.
|
|
599
|
+
|
|
600
|
+
When optional argument VISIBLE-ONLY is non-nil, don't export
|
|
601
|
+
contents of hidden elements.
|
|
602
|
+
|
|
603
|
+
Export is done in a buffer named \"*Org Gemini Export*\", which will
|
|
604
|
+
be displayed when `org-export-show-temporary-export-buffer' is
|
|
605
|
+
non-nil."
|
|
606
|
+
(interactive)
|
|
607
|
+
(org-export-to-buffer
|
|
608
|
+
'gmi "*Org Gemini Export*"
|
|
609
|
+
async subtreep visible-only nil nil
|
|
610
|
+
(lambda ()
|
|
611
|
+
(if (featurep 'gemini-mode)
|
|
612
|
+
(gemini-mode)
|
|
613
|
+
(text-mode)))))
|
|
614
|
+
|
|
615
|
+
;;;###autoload
|
|
616
|
+
(defun org-gmi-export-to-gemini (&optional async subtreep visible-only)
|
|
617
|
+
"Export current buffer to a Gemini file.
|
|
618
|
+
|
|
619
|
+
If narrowing is active in the current buffer, only export its
|
|
620
|
+
narrowed part.
|
|
621
|
+
|
|
622
|
+
If a region is active, export that region.
|
|
623
|
+
|
|
624
|
+
A non-nil optional argument ASYNC means the process should happen
|
|
625
|
+
asynchronously. The resulting file should be accessible through
|
|
626
|
+
the `org-export-stack' interface.
|
|
627
|
+
|
|
628
|
+
When optional argument SUBTREEP is non-nil, export the sub-tree
|
|
629
|
+
at point, extracting information from the headline properties
|
|
630
|
+
first.
|
|
631
|
+
|
|
632
|
+
When optional argument VISIBLE-ONLY is non-nil, don't export
|
|
633
|
+
contents of hidden elements.
|
|
634
|
+
|
|
635
|
+
Return output file's name."
|
|
636
|
+
(interactive)
|
|
637
|
+
(let ((outfile (org-export-output-file-name ".gmi" subtreep)))
|
|
638
|
+
(org-export-to-file 'gmi outfile async subtreep visible-only)))
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
;;;###autoload
|
|
642
|
+
(defun org-gmi-publish-to-gemini (plist filename pub-dir)
|
|
643
|
+
"Publish an org file to Gemini.
|
|
644
|
+
|
|
645
|
+
FILENAME is the filename of the Org file to be published. PLIST
|
|
646
|
+
is the property list for the given project. PUB-DIR is the
|
|
647
|
+
publishing directory.
|
|
648
|
+
|
|
649
|
+
Return output file name."
|
|
650
|
+
(org-publish-org-to 'gmi filename ".gmi" plist pub-dir))
|
|
651
|
+
|
|
652
|
+
(provide 'ox-gmi)
|
|
653
|
+
|
|
654
|
+
;;; ox-gmi.el ends here
|
data/lib/fronde/config/lisp.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
-
require 'open-uri'
|
|
5
4
|
require_relative '../version'
|
|
6
5
|
require_relative '../org'
|
|
7
6
|
require_relative 'helpers'
|
|
@@ -11,6 +10,19 @@ module Fronde
|
|
|
11
10
|
# This module contains utilitary methods to ease ~org-config.el~
|
|
12
11
|
# file generation
|
|
13
12
|
module Lisp
|
|
13
|
+
class << self
|
|
14
|
+
def theme_directory(theme)
|
|
15
|
+
# User theme first to allow overwriting
|
|
16
|
+
directory = File.expand_path("themes/#{theme}")
|
|
17
|
+
return directory if Dir.exist? directory
|
|
18
|
+
|
|
19
|
+
directory = File.expand_path("data/themes/#{theme}", __dir__)
|
|
20
|
+
return directory if Dir.exist? directory
|
|
21
|
+
|
|
22
|
+
raise Errno::ENOENT, "Theme #{theme} not found"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
# Generate emacs lisp configuration file for Org and write it.
|
|
15
27
|
#
|
|
16
28
|
# This method saves the generated configuration in the file
|
|
@@ -41,19 +53,8 @@ module Fronde
|
|
|
41
53
|
|
|
42
54
|
private
|
|
43
55
|
|
|
44
|
-
def theme_directory(theme)
|
|
45
|
-
# User theme first to allow overwriting
|
|
46
|
-
directory = File.expand_path("themes/#{theme}")
|
|
47
|
-
return directory if Dir.exist? directory
|
|
48
|
-
|
|
49
|
-
directory = File.expand_path("data/themes/#{theme}", __dir__)
|
|
50
|
-
return directory if Dir.exist? directory
|
|
51
|
-
|
|
52
|
-
raise Errno::ENOENT, "Theme #{theme} not found"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
56
|
def org_theme_config(theme)
|
|
56
|
-
{ 'base-directory' => theme_directory(theme),
|
|
57
|
+
{ 'base-directory' => Lisp.theme_directory(theme),
|
|
57
58
|
# rubocop:disable Layout/LineLength
|
|
58
59
|
'base-extension' => %w[css js gif jpg png svg otf ttf woff2?].join('\\\\|'),
|
|
59
60
|
'publishing-directory' => "#{get('html_public_folder')}/assets/#{theme}",
|
|
@@ -34,21 +34,16 @@ module Fronde
|
|
|
34
34
|
# @param entries [Array] the article to list in this file
|
|
35
35
|
# @return [String] the Atom feed as a String
|
|
36
36
|
def atom_file(tag_name, entries)
|
|
37
|
-
domain = Fronde::CONFIG.get('domain')
|
|
38
37
|
slug = Slug.slug(tag_name)
|
|
39
|
-
|
|
40
|
-
Config::Helpers.render_liquid_template(
|
|
41
|
-
File.read(File.expand_path('./data/template.xml', __dir__)),
|
|
38
|
+
variables = atom_templating_basics.merge(
|
|
42
39
|
'title' => @tags_names[tag_name],
|
|
43
|
-
'lang' => Fronde::CONFIG.get('lang'),
|
|
44
|
-
'domain' => domain,
|
|
45
40
|
'slug' => slug,
|
|
46
|
-
'tagurl' => tagurl,
|
|
47
|
-
'upddate' => @date.xmlschema,
|
|
48
|
-
'author' => Fronde::CONFIG.get('author'),
|
|
49
|
-
'publication_format' => @project['mime_type'],
|
|
50
41
|
'entries' => entries
|
|
51
42
|
)
|
|
43
|
+
Config::Helpers.render_liquid_template(
|
|
44
|
+
File.read(File.expand_path('./data/template.xml', __dir__)),
|
|
45
|
+
variables
|
|
46
|
+
)
|
|
52
47
|
end
|
|
53
48
|
|
|
54
49
|
# Render the main/index Atom feed.
|
|
@@ -56,19 +51,26 @@ module Fronde
|
|
|
56
51
|
# @param entries [Array] the article to list in this file
|
|
57
52
|
# @return [String] the Atom feed as a String
|
|
58
53
|
def atom_index(entries)
|
|
59
|
-
|
|
54
|
+
variables = atom_templating_basics.merge(
|
|
55
|
+
'title' => @project['title'],
|
|
56
|
+
'slug' => '__HOME_PAGE__',
|
|
57
|
+
'entries' => entries
|
|
58
|
+
)
|
|
60
59
|
Config::Helpers.render_liquid_template(
|
|
61
60
|
File.read(File.expand_path('./data/template.xml', __dir__)),
|
|
62
|
-
|
|
61
|
+
variables
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def atom_templating_basics
|
|
66
|
+
{
|
|
63
67
|
'lang' => Fronde::CONFIG.get('lang'),
|
|
64
|
-
'domain' => domain,
|
|
65
|
-
'slug' => 'index',
|
|
66
|
-
'tagurl' => domain,
|
|
67
|
-
'upddate' => @date.xmlschema,
|
|
68
68
|
'author' => Fronde::CONFIG.get('author'),
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
|
|
69
|
+
'domain' => Fronde::CONFIG.get('domain'),
|
|
70
|
+
'project_path' => @project.public_absolute_path,
|
|
71
|
+
'upddate' => @date.xmlschema,
|
|
72
|
+
'publication_format' => @project['mime_type']
|
|
73
|
+
}
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
end
|
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
xml:lang="{{ lang }}">
|
|
5
5
|
|
|
6
6
|
<title>{{ title | escape }}</title>
|
|
7
|
-
|
|
8
|
-
<link href="{{
|
|
7
|
+
{%- if slug == "__HOME_PAGE__" %}
|
|
8
|
+
<link href="{{ domain }}{{ project_path }}feeds/index.xml" rel="self" type="application/atom+xml"/>
|
|
9
|
+
<link href="{{ domain }}" rel="alternate" type="text/html" title="{{ title | escape }}"/>
|
|
10
|
+
{%- else %}
|
|
11
|
+
<link href="{{ domain }}{{ project_path }}feeds/{{ slug }}.xml" rel="self" type="application/atom+xml"/>
|
|
12
|
+
<link href="{{ domain }}{{ project_path }}tags/{{ slug }}.html" rel="alternate" type="text/html" title="{{ title | escape }}"/>
|
|
13
|
+
{%- endif %}
|
|
9
14
|
<updated>{{ upddate }}</updated>
|
|
10
15
|
<author><name>{{ author }}</name></author>
|
|
11
16
|
<id>urn:md5:{{ domain | md5 }}</id>
|
|
12
|
-
<generator uri="https://git.umaneti.net/fronde
|
|
17
|
+
<generator uri="https://git.umaneti.net/fronde">Fronde</generator>
|
|
13
18
|
|
|
14
19
|
{%- for article in entries %}
|
|
15
20
|
|
|
@@ -17,7 +22,7 @@
|
|
|
17
22
|
<title>{{ article.title | escape }}</title>
|
|
18
23
|
<link href="{{ article.url }}" rel="alternate"
|
|
19
24
|
type="{{ publication_format }}"
|
|
20
|
-
title="{{ article.title }}"/>
|
|
25
|
+
title="{{ article.title | escape }}"/>
|
|
21
26
|
<id>urn:md5:{{ article.timekey | md5 }}</id>
|
|
22
27
|
<published>{{ article.published_xml }}</published>
|
|
23
28
|
<updated>{{ article.updated_xml }}</updated>
|
data/lib/fronde/org/file.rb
CHANGED
|
@@ -193,7 +193,7 @@ module Fronde
|
|
|
193
193
|
.gsub('%l', @data[:lang])
|
|
194
194
|
.gsub('%L', Fronde::CONFIG.get('license', '').gsub(/\s+/, ' ').strip)
|
|
195
195
|
.gsub('%n', "Fronde #{Fronde::VERSION}")
|
|
196
|
-
.gsub('%N', "<a href=\"https://git.umaneti.net/fronde
|
|
196
|
+
.gsub('%N', "<a href=\"https://git.umaneti.net/fronde\">Fronde</a> #{Fronde::VERSION}")
|
|
197
197
|
.gsub('%o', project_data['theme'] || '')
|
|
198
198
|
.gsub('%s', @data[:subtitle])
|
|
199
199
|
.gsub('%t', @data[:title])
|
data/lib/fronde/org.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require_relative 'version'
|
|
5
|
+
|
|
3
6
|
module Fronde
|
|
4
7
|
# Everything related to Org mode
|
|
5
8
|
#
|
|
@@ -7,6 +10,8 @@ module Fronde
|
|
|
7
10
|
# of the Emacs package. It also serves as a namespace for the class
|
|
8
11
|
# responsible for handling Org files: {Fronde::Org::File}.
|
|
9
12
|
module Org
|
|
13
|
+
CGIT_BASE_URL = 'https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/'
|
|
14
|
+
|
|
10
15
|
class << self
|
|
11
16
|
def current_version
|
|
12
17
|
# Do not crash if Org is not yet installed (and thus return nil)
|
|
@@ -36,18 +41,26 @@ module Fronde
|
|
|
36
41
|
org_version
|
|
37
42
|
end
|
|
38
43
|
|
|
44
|
+
def http_get_client(uri)
|
|
45
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
46
|
+
request = Net::HTTP::Get.new(uri)
|
|
47
|
+
request['User-Agent'] = Fronde::USER_AGENT
|
|
48
|
+
yield http, request
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
39
52
|
def fetch_version_number
|
|
40
53
|
# Retrieve last org version from git repository tags page.
|
|
41
54
|
tag_rx = Regexp.new(
|
|
42
55
|
'<a href=\'/cgit/emacs/org-mode.git/tag/\?h=' \
|
|
43
56
|
'(?<tag>release_(?<number>[^\']+))\'>\k<tag></a>'
|
|
44
57
|
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
uri = URI(CGIT_BASE_URL)
|
|
59
|
+
response = http_get_client(uri) { |http, req| http.request req }
|
|
60
|
+
versions = response.body.each_line(chomp: true).filter_map do |line|
|
|
48
61
|
line.match(tag_rx) { |matchdata| matchdata[:number] }
|
|
49
62
|
end
|
|
50
|
-
versions.compact.
|
|
63
|
+
versions.compact.max_by { Gem::Version.new _1 }
|
|
51
64
|
end
|
|
52
65
|
|
|
53
66
|
# Download latest org-mode tarball.
|
|
@@ -57,10 +70,10 @@ module Fronde
|
|
|
57
70
|
def download(destination = 'var/tmp')
|
|
58
71
|
org_last_version = last_version(force: false, cookie_dir: destination)
|
|
59
72
|
tarball = "org-mode-release_#{org_last_version}.tar.gz"
|
|
60
|
-
uri = URI("
|
|
73
|
+
uri = URI("#{CGIT_BASE_URL}snapshot/#{tarball}")
|
|
61
74
|
# Will crash on purpose if anything goes wrong
|
|
62
|
-
|
|
63
|
-
fetch_org_tarball http,
|
|
75
|
+
http_get_client(uri) do |http, request|
|
|
76
|
+
fetch_org_tarball http, request, destination
|
|
64
77
|
end
|
|
65
78
|
org_last_version
|
|
66
79
|
end
|
data/lib/fronde/version.rb
CHANGED
|
@@ -2,5 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Fronde
|
|
4
4
|
# @return [String] the version number of the current Fronde release.
|
|
5
|
-
VERSION = '0.6.
|
|
5
|
+
VERSION = '0.6.3'
|
|
6
|
+
|
|
7
|
+
USER_AGENT = ["Fronde/#{Fronde::VERSION}",
|
|
8
|
+
"(#{RUBY_ENGINE} #{RUBY_VERSION} #{RUBY_PLATFORM})",
|
|
9
|
+
'(+https://etienne.pflieger.bzh/fronde/)'].join(' ').freeze
|
|
6
10
|
end
|
data/lib/tasks/org.rake
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'open-uri'
|
|
4
|
-
|
|
5
3
|
require_relative '../fronde/config'
|
|
6
4
|
require_relative '../fronde/cli/throbber'
|
|
7
5
|
|
|
@@ -12,7 +10,6 @@ CLOBBER.push(
|
|
|
12
10
|
)
|
|
13
11
|
|
|
14
12
|
HTMLIZE_TAG = 'release/1.58'
|
|
15
|
-
OX_GMI_TAG = 'v0.2'
|
|
16
13
|
|
|
17
14
|
namespace :org do
|
|
18
15
|
directory 'var/tmp'
|
|
@@ -57,20 +54,18 @@ namespace :org do
|
|
|
57
54
|
directory 'lib'
|
|
58
55
|
|
|
59
56
|
file 'lib/htmlize.el' => 'lib' do
|
|
60
|
-
|
|
57
|
+
uri = URI(
|
|
61
58
|
"https://raw.githubusercontent.com/hniksic/emacs-htmlize/refs/tags/#{HTMLIZE_TAG}/htmlize.el"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
).open.read
|
|
70
|
-
File.write 'lib/ox-gmi.el', ox_gmi
|
|
59
|
+
)
|
|
60
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
61
|
+
request = Net::HTTP::Get.new(uri)
|
|
62
|
+
request['User-Agent'] = Fronde::USER_AGENT
|
|
63
|
+
http.request request
|
|
64
|
+
end
|
|
65
|
+
File.write 'lib/htmlize.el', response.body
|
|
71
66
|
end
|
|
72
67
|
|
|
73
|
-
file 'var/lib/org-config.el' => ['lib/htmlize.el'
|
|
68
|
+
file 'var/lib/org-config.el' => ['lib/htmlize.el'] do
|
|
74
69
|
Fronde::CONFIG.write_org_lisp_config
|
|
75
70
|
end
|
|
76
71
|
|
|
@@ -85,9 +80,9 @@ namespace :org do
|
|
|
85
80
|
|
|
86
81
|
desc 'Install Org'
|
|
87
82
|
multitask install: ['org:compile', '.gitignore'] do
|
|
88
|
-
# lib/htmlize.el
|
|
89
|
-
#
|
|
90
|
-
#
|
|
83
|
+
# lib/htmlize.el cannot be generated in parallel of org:compilation,
|
|
84
|
+
# as it will leads to a weird SSL error. Thus finishing file generation
|
|
85
|
+
# "manually" here.
|
|
91
86
|
Rake::Task['var/lib/org-config.el'].invoke
|
|
92
87
|
sources = Fronde::CONFIG.sources
|
|
93
88
|
sources.each { mkdir_p _1['path'] }
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fronde
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- Étienne
|
|
8
|
-
autorequire:
|
|
7
|
+
- Étienne Pflieger
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: base64
|
|
@@ -58,28 +57,28 @@ dependencies:
|
|
|
58
57
|
requirements:
|
|
59
58
|
- - "~>"
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '5.
|
|
60
|
+
version: '5.8'
|
|
62
61
|
type: :runtime
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
65
|
- - "~>"
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '5.
|
|
67
|
+
version: '5.8'
|
|
69
68
|
- !ruby/object:Gem::Dependency
|
|
70
69
|
name: nokogiri
|
|
71
70
|
requirement: !ruby/object:Gem::Requirement
|
|
72
71
|
requirements:
|
|
73
72
|
- - "~>"
|
|
74
73
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '1.
|
|
74
|
+
version: '1.18'
|
|
76
75
|
type: :runtime
|
|
77
76
|
prerelease: false
|
|
78
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
78
|
requirements:
|
|
80
79
|
- - "~>"
|
|
81
80
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '1.
|
|
81
|
+
version: '1.18'
|
|
83
82
|
- !ruby/object:Gem::Dependency
|
|
84
83
|
name: rainbow
|
|
85
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,28 +99,28 @@ dependencies:
|
|
|
100
99
|
requirements:
|
|
101
100
|
- - "~>"
|
|
102
101
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '13.
|
|
102
|
+
version: '13.2'
|
|
104
103
|
type: :runtime
|
|
105
104
|
prerelease: false
|
|
106
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
106
|
requirements:
|
|
108
107
|
- - "~>"
|
|
109
108
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '13.
|
|
109
|
+
version: '13.2'
|
|
111
110
|
- !ruby/object:Gem::Dependency
|
|
112
111
|
name: webrick
|
|
113
112
|
requirement: !ruby/object:Gem::Requirement
|
|
114
113
|
requirements:
|
|
115
114
|
- - "~>"
|
|
116
115
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '1.
|
|
116
|
+
version: '1.9'
|
|
118
117
|
type: :runtime
|
|
119
118
|
prerelease: false
|
|
120
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
120
|
requirements:
|
|
122
121
|
- - "~>"
|
|
123
122
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '1.
|
|
123
|
+
version: '1.9'
|
|
125
124
|
description: |
|
|
126
125
|
Fronde helps you to convert Org mode files into websites, giving you
|
|
127
126
|
full control over the publication process.
|
|
@@ -149,6 +148,7 @@ files:
|
|
|
149
148
|
- lib/fronde/config.rb
|
|
150
149
|
- lib/fronde/config/data/org-config.el
|
|
151
150
|
- lib/fronde/config/data/ox-fronde.el
|
|
151
|
+
- lib/fronde/config/data/ox-gmi.el
|
|
152
152
|
- lib/fronde/config/data/themes/umaneti/css/htmlize.css
|
|
153
153
|
- lib/fronde/config/data/themes/umaneti/css/style.css
|
|
154
154
|
- lib/fronde/config/data/themes/umaneti/img/bottom.png
|
|
@@ -184,13 +184,13 @@ files:
|
|
|
184
184
|
- lib/tasks/tags.rake
|
|
185
185
|
- locales/en.yml
|
|
186
186
|
- locales/fr.yml
|
|
187
|
-
homepage: https://
|
|
187
|
+
homepage: https://etienne.pflieger.bzh/fronde/
|
|
188
188
|
licenses:
|
|
189
189
|
- WTFPL
|
|
190
190
|
metadata:
|
|
191
191
|
rubygems_mfa_required: 'true'
|
|
192
192
|
source_code_uri: https://git.umaneti.net/fronde
|
|
193
|
-
homepage_uri: https://etienne.
|
|
193
|
+
homepage_uri: https://etienne.pflieger.bzh/fronde/
|
|
194
194
|
funding_uri: https://liberapay.com/milouse
|
|
195
195
|
post_install_message: |+
|
|
196
196
|
Start your first fronde project with:
|
|
@@ -217,8 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
217
217
|
version: '0'
|
|
218
218
|
requirements:
|
|
219
219
|
- emacs
|
|
220
|
-
rubygems_version: 3.
|
|
221
|
-
signing_key:
|
|
220
|
+
rubygems_version: 3.7.2
|
|
222
221
|
specification_version: 4
|
|
223
222
|
summary: An opinionated static website generator for Org
|
|
224
223
|
test_files: []
|