forked from msnoigrs/ox-rst
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ox-rst.el
1680 lines (1400 loc) · 57.9 KB
/
ox-rst.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; ox-rst.el --- Export reStructuredText using org-mode.
;; Copyright (C) 2015 IGARASHI Masanao
;; 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 2 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, write to the Free Software
;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
;; Author: IGARASHI Masanao <[email protected]>
;; Keywords: org, rst, reST, reStructuredText
;; Version: 0.2
;; URL: https://github.com/masayuko/ox-rst
;; Package-Requires: ((emacs "24.4") (org "8.2.4"))
;;; Commentary:
;; This library implements an reStructuredText back-end for
;; Org generic exporter.
;;; Code:
(eval-when-compile (require 'cl))
(require 'ox)
(require 'ox-publish)
(require 'ox-ascii)
;;; Define Back-End
(org-export-define-backend 'rst
'((bold . org-rst-bold)
(center-block . org-rst-center-block)
(clock . org-rst-clock)
(code . org-rst-code)
(drawer . org-rst-drawer)
(dynamic-block . org-rst-dynamic-block)
(entity . org-rst-entity)
(example-block . org-rst-example-block)
(export-block . org-rst-export-block)
(export-snippet . org-rst-export-snippet)
(fixed-width . org-rst-fixed-width)
(footnote-reference . org-rst-footnote-reference)
(headline . org-rst-headline)
(horizontal-rule . org-rst-horizontal-rule)
(inline-src-block . org-rst-inline-src-block)
(inlinetask . org-rst-inlinetask)
(inner-template . org-rst-inner-template)
(italic . org-rst-italic)
(item . org-rst-item)
(keyword . org-rst-keyword)
(latex-environment . org-rst-latex-environment)
(latex-fragment . org-rst-latex-fragment)
(line-break . org-rst-line-break)
(link . org-rst-link)
(node-property . org-rst-node-property)
(paragraph . org-rst-paragraph)
(plain-list . org-rst-plain-list)
(plain-text . org-rst-plain-text)
(planning . org-rst-planning)
(property-drawer . org-rst-property-drawer)
(quote-block . org-rst-quote-block)
(radio-target . org-rst-radio-target)
(section . org-rst-section)
(special-block . org-rst-special-block)
(src-block . org-rst-src-block)
(statistics-cookie . org-rst-statistics-cookie)
(strike-through . org-rst-strike-through)
(subscript . org-rst-subscript)
(superscript . org-rst-superscript)
(table . org-rst-table)
(table-cell . org-rst-table-cell)
(table-row . org-rst-table-row)
(target . org-rst-target)
(template . org-rst-template)
(timestamp . org-rst-timestamp)
(underline . org-rst-underline)
(verbatim . org-rst-verbatim)
(verse-block . org-rst-verse-block)
;; Pseudo objects and elements.
(latex-math-block . org-rst-math-block))
:export-block '("RST" "REST" "RESTRUCTUREDTEXT")
:menu-entry
'(?r "Export to reStructuredText"
((?R "As reStructuredText buffer" org-rst-export-as-rst)
(?r "As reStructuredText file" org-rst-export-to-rst)))
:options-alist
'((:subtitle "SUBTITLE" nil nil parse)
(:rst-link-org-as-html nil nil org-rst-link-org-files-as-html)
(:rst-link-use-abs-url nil "rst-link-use-abs-url" org-rst-link-use-abs-url)
(:rst-inline-images nil nil org-rst-inline-images)
(:rst-inline-image-rules nil nil org-rst-inline-image-rules)
(:rst-link-org-files-as-html nil nil org-rst-link-org-files-as-html)
(:rst-link-home "RST_LINK_HOME" nil org-rst-link-home)
(:rst-link-use-ref-role nil nil org-rst-link-use-ref-role))
:filters-alist '((:filter-options . org-rst-math-block-options-filter)
(:filter-headline . org-rst-filter-headline-blank-lines)
(:filter-parse-tree org-rst-math-block-tree-filter
org-rst-separate-elements
org-rst-filter-paragraph-spacing)
(:filter-section . org-rst-filter-headline-blank-lines)))
;;; Internal Variables
;;; User Configurable Variables
(defgroup org-export-rst nil
"Options for exporting Org mode files to reStructuredText."
:tag "Org RST"
:group 'org-export)
(defcustom org-rst-link-org-files-as-html t
"Non-nil means make file links to `file.org' point to `file.html'.
When `org-mode' is exporting an `org-mode' file to HTML, links to
non-html files are directly put into a href tag in HTML.
However, links to other Org-mode files (recognized by the
extension `.org.) should become links to the corresponding html
file, assuming that the linked `org-mode' file will also be
converted to HTML.
When nil, the links still point to the plain `.org' file."
:group 'org-export-rst
:type 'boolean)
(defcustom org-rst-link-home ""
"Where should the \"HOME\" link of exported HTML pages lead?"
:group 'org-export-rst
:type '(string :tag "File or URL"))
(defcustom org-rst-link-use-abs-url nil
"Should we prepend relative links with RST_LINK_HOME?"
:group 'org-export-rst
:type 'boolean)
;;;; Links :: Inline images
(defcustom org-rst-inline-images t
"Non-nil means inline images into exported reStructuredText.
This is done using an image directive or an figure directive.
When nil, an anchor with reference is used to link to the image."
:group 'org-export-rst
:type 'boolean)
(defcustom org-rst-inline-image-rules
'(("file" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\|svgz\\|swf\\)\\'")
("fuzzy" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\|svgz\\|swf\\)\\'")
("http" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\|svgz\\|swf\\)\\'")
("https" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\|svgz\\|swf\\)\\'"))
"Rules characterizing image files that can be inlined into reStructuredText.
A rule consists in an association whose key is the type of link
to consider, and value is a regexp that will be matched against
link's path."
:group 'org-export-rst
:type '(alist :key-type (string :tag "Type")
:value-type (regexp :tag "Path")))
(defcustom org-rst-link-use-ref-role nil
"Non-nil means export internal links using :ref: role."
:group 'org-export-rst
:type 'boolean)
(defcustom org-rst-text-markup-alist '((bold . "**%s**")
(code . verb)
(italic . "*%s*")
(verbatim . verb)
(comment . ".. %s\n"))
"Alist of reStructredText expressions to convert text markup.
The key must be a symbol among `bold', `code', `italic',
`comment' and `verbatim'. The value is a formatting string to
wrap fontified text with.
Value can also be set to the following symbols: `verb'.
If no association can be found for a given markup, text will be
returned as-is."
:group 'org-export-rst
:type 'alist
:options '(bold code italic verbatim comment))
(defcustom org-rst-quote-margin 4
"Width of margin used for quoting text, in characters.
This margin is applied on left side of the text."
:group 'org-export-rst
:type 'integer)
(defcustom org-rst-headline-spacing '(1 . 1)
"Number of blank lines inserted around headlines.
This variable can be set to a cons cell. In that case, its car
represents the number of blank lines present before headline
contents whereas its cdr reflects the number of blank lines after
contents.
A nil value replicates the number of blank lines found in the
original Org buffer at the same place."
:group 'org-export-rst
:type '(choice
(const :tag "Replicate original spacing" nil)
(cons :tag "Set a uniform spacing"
(integer :tag "Number of blank lines before contents")
(integer :tag "Number of blank lines after contents"))))
(defcustom org-rst-paragraph-spacing 'auto
"Number of white lines between paragraphs.
If the value is an integer, add this number of blank lines
between contiguous paragraphs. If is it the symbol `auto', keep
the same number of blank lines as in the original document."
:group 'org-export-rst
:type '(choice
(integer :tag "Number of blank lines")
(const :tag "Preserve original spacing" auto)))
;;;; Drawers
(defcustom org-rst-format-drawer-function nil
"Function called to format a drawer in reStructuredText code.
The function must accept two parameters:
NAME the drawer name, like \"LOGBOOK\"
CONTENTS the contents of the drawer.
The function should return the string to be exported.
For example, the variable could be set to the following function
in order to mimic default behaviour:
\(defun org-rst-format-drawer-default \(name contents\)
\"Format a drawer element for reStructuredText export.\"
contents\)"
:group 'org-export-rst
:type 'function)
;;;; Inlinetasks
(defcustom org-rst-format-inlinetask-function nil
"Function called to format an inlinetask in reStructuredText code.
The function must accept six parameters:
TODO the todo keyword, as a string
TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
PRIORITY the inlinetask priority, as a string
NAME the inlinetask name, as a string.
TAGS the inlinetask tags, as a list of strings.
CONTENTS the contents of the inlinetask, as a string.
The function should return the string to be exported."
:group 'org-export-rst
:type 'function)
;;;; Src blocks
(defcustom org-rst-pygments t
"Non-nil means export source code using the pygments package."
:group 'org-export-rst
:type '(choice
(const :tag "Use pygments" t)
(const :tag "Export verbatim" nil)))
(defcustom org-rst-pygments-langs
'((emacs-lisp "scheme") (lisp "scheme") (clojure "clojure")
(c "c") (cc "cpp")
(fortran "fortran")
(perl "perl") (cperl "perl") (python "python") (ruby "ruby")
(html "html") (xml "xml")
(tex "tex") (latex "latex")
(shell-script "bash")
(gnuplot "gnuplot")
(ocaml "ocaml") (caml "ocaml")
(sql "sql") (sqlite "sqlite3"))
"Alist mapping languages to their listing language counterpart.
The key is a symbol, the major mode symbol without the \"-mode\".
The value is the string that should be inserted as the language
parameter for the listings package. If the mode name and the
listings name are the same, the language does not need an entry
in this list - but it does not hurt if it is present."
:group 'org-export-rst
:type '(repeat
(list
(symbol :tag "Major mode ")
(string :tag "Pygments language"))))
;;; Internal Functions
(defun org-rst--indent-string (s width)
"Indent string S by WIDTH white spaces.
Empty lines are not indented."
(when (stringp s)
(replace-regexp-in-string
"\\(^\\)\\(?:.*\\S-\\)" (make-string width ? ) s nil nil 1)))
(defun org-rst--has-caption-p (element info)
"Non-nil when ELEMENT has a caption affiliated keyword.
INFO is a plist used as a communication channel. This function
is meant to be used as a predicate for `org-export-get-ordinal'."
(org-element-property :caption element))
(defun org-rst--make-attribute-string (attributes)
"Return a list of attributes, as a string.
ATTRIBUTES is a plist where values are either strings or nil. An
attributes with a nil value will be omitted from the result."
(let (output)
(dolist (item attributes (mapconcat 'identity (nreverse output) "\n"))
(cond ((null item) (pop output))
((symbolp item) (push (substring (symbol-name item) 1) output))
(t (let ((key (org-trim (car output)))
(value (replace-regexp-in-string "\"" "\\\""
(replace-regexp-in-string
"\\\\" "\\\\" (org-trim item)))))
(setcar output (format " :%s: %s" key value))))))))
(defun org-rst--build-title
(element info &optional underline notags toc)
"Format ELEMENT title and return it.
ELEMENT is either an `headline' or `inlinetask' element. INFO is
a plist used as a communication channel.
When optional argument UNDERLINE is non-nil, underline title,
without the tags, according to `org-rst-underline'
specifications.
If optional argument NOTAGS is non-nil, no tags will be added to
the title.
When optional argument TOC is non-nil, use optional title if
possible. It doesn't apply to `inlinetask' elements."
(let* ((headlinep (eq (org-element-type element) 'headline))
(numbers
;; Numbering is specific to headlines.
(and headlinep (org-export-numbered-headline-p element info)
;; All tests passed: build numbering string.
(concat
(mapconcat
'number-to-string
(org-export-get-headline-number element info) ".")
" ")))
(text
(org-trim
(org-export-data
(if (and toc headlinep) (org-export-get-alt-title element info)
(org-element-property :title element))
info)))
(todo
(and (plist-get info :with-todo-keywords)
(let ((todo (org-element-property :todo-keyword element)))
(and todo (concat (org-export-data todo info) " ")))))
(tags (and (not notags)
(plist-get info :with-tags)
(let ((tag-list (org-export-get-tags element info)))
(and tag-list
(format ":%s:"
(mapconcat 'identity tag-list ":"))))))
(priority
(and (plist-get info :with-priority)
(let ((char (org-element-property :priority element)))
(and char (format "(#%c) " char)))))
(first-part (concat numbers todo priority text)))
(concat
first-part
;; Align tags, if any.
(when tags
(format
(format " %%%ds" (string-width tags))
tags))
;; Maybe underline text, if ELEMENT type is `headline' and an
;; underline character has been defined.
(when (and underline headlinep)
(let ((under-char
(nth (1- (org-export-get-relative-level element info))
'(?- ?~ ?^ ?: ?' ?\ ?_))))
(and under-char
(concat "\n"
(make-string (string-width first-part) under-char))))))))
(defun org-rst--text-markup (text markup)
"Format TEXT depending on MARKUP text markup.
See `org-rst-text-markup-alist' for details."
(let ((fmt (cdr (assq markup org-rst-text-markup-alist))))
(cond
;; No format string: Return raw text.
((not fmt) text)
;; Handle the `verb' special case: Protect some
;; special chars and use "\\" escape.
((eq 'verb fmt)
(let ((start 0)
(rtn "")
char)
(while (string-match "\\`*" text)
(setq char (match-string 0 text))
(if (> (match-beginning 0) 0)
(setq rtn (concat rtn (substring text 0 (match-beginning 0)))))
(setq text (substring text (1+ (match-beginning 0))))
(setq char (concat "\\" char)
rtn (concat rtn char)))
(setq text (concat rtn text)
fmt "``%s``")
(format fmt text)))
;; Else use format string.
(t (format fmt text)))))
(defun org-rst--checkbox (item info)
"Return checkbox string for ITEM or nil.
INFO is a plist used as a communication channel."
;(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
; (case (org-element-property :checkbox item)
; (on (if utf8p "☑ " "[X] "))
; (off (if utf8p "☐ " "[ ] "))
; (trans (if utf8p "☒ " "[-] ")))))
(case (org-element-property :checkbox item)
(on "☑ ")
(off "☐ ")
(trans "☒ ")))
;;; Template
(defun org-rst-template--document-title (info)
"Return document title, as a string.
INFO is a plist used as a communication channel."
(let* (;; Links in the title will not be resolved later, so we make
;; sure their path is located right after them.
(with-title (plist-get info :with-title))
(title (if with-title
(org-export-data (plist-get info :title) info)
""))
(subtitle (if with-title
(org-export-data (plist-get info :subtitle) info)
""))
(author (and (plist-get info :with-author)
(let ((auth (plist-get info :author)))
(and auth (org-export-data auth info)))))
(email (and (plist-get info :with-email)
(org-export-data (plist-get info :email) info)))
(date (and (plist-get info :with-date)
(org-export-data (org-export-get-date info) info)))
(title
(if (string= title "")
(cond
((and (org-string-nw-p date) (org-string-nw-p author))
(concat
author
date
(when (org-string-nw-p email) email)))
((and (org-string-nw-p date) (org-string-nw-p email))
(concat
email
date
date))
((org-string-nw-p date)
date)
((and (org-string-nw-p author) (org-string-nw-p email))
(concat author email))
((org-string-nw-p author) author)
((org-string-nw-p email) email)) title))
(titleline (make-string (string-width title) ?=))
(subtitleline (make-string (string-width subtitle) ?-))
(subtitle (if (not (string= subtitle ""))
(concat subtitleline "\n"
subtitle "\n"
subtitleline "\n") "")))
(concat
titleline "\n"
title "\n"
titleline "\n"
subtitle
(when (org-string-nw-p author) (concat "\n :Author: " author))
(when (org-string-nw-p email) (concat "\n :Contact: " email))
(when (org-string-nw-p date) (concat "\n :Date: " date))
"\n")))
(defun org-rst-template (contents info)
"Return complete document string after reStructuredText conversion.
CONTENTS is the transcoded contents string. INFO is a plist
holding export options."
(concat
;; Build title block.
(concat (org-rst-template--document-title info)
;; 2. Table of contents.
(let ((depth (plist-get info :with-toc)))
(when depth "\n.. contents::\n")))
;; Document's body.
contents
;; Creator. Justify it to the bottom right.
(and (plist-get info :with-creater)
(concat
"\n :Creator: "
(plist-get info :creator) "\n"))))
;;; Transcode Functions
;;;; Bold
(defun org-rst-bold (bold contents info)
"Transcode BOLD from Org to reStructuredText.
CONTENTS is the text with bold markup. INFO is a plist holding
contextual information."
(org-rst--text-markup contents 'bold))
;;;; Center Block
(defun org-rst-center-block (center-block contents info)
"Transcode a CENTER-BLOCK element from Org to reStructuredText.
CONTENTS holds the contents of the center block. INFO is a plist
holding contextual information."
contents)
;;;; Clock
(defun org-rst-clock (clock contents info)
"Transcode a CLOCK object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual
information."
(concat org-clock-string " "
(org-translate-time
(org-element-property :raw-value
(org-element-property :value clock)))
(let ((time (org-element-property :duration clock)))
(and time
(concat " => "
(apply 'format
"%2s:%02s"
(org-split-string time ":")))))))
;;;; Code
(defun org-rst-code (code contents info)
"Transcode a CODE object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist used as a communication
channel."
(org-rst--text-markup (org-element-property :value code) 'code))
;;; Comment
(defun org-rst-comment (comment contents info)
"Transcode a COMMENT object from Org to reStructuredText.
CONTENTS is the text in the comment. INFO is a plist holding
contextual information."
(org-rst--text-markup (org-element-property :value comment) 'comment))
;;; Comment Block
(defun org-rst-comment-block (comment-block contents info)
"Transcode a COMMENT-BLOCK object from Org to reStructuredText.
CONTENTS is the text within the block. INFO is a plist holding
contextual information."
(let ((value (org-remove-indentation
(org-element-property :value comment-block))))
(when value
(concat "..\n" (org-rst--indent-string value org-rst-quote-margin)))))
;;;; Drawer
(defun org-rst-drawer (drawer contents info)
"Transcode a DRAWER element from Org to reStructuredText.
CONTENTS holds the contents of the block. INFO is a plist
holding contextual information."
(let* ((name (org-element-property :drawer-name drawer))
(output (if (functionp org-rst-format-drawer-function)
(funcall org-rst-format-drawer-function
name contents)
;; If there's no user defined function: simply
;; display contents of the drawer.
contents)))
output))
;;;; Dynamic Block
(defun org-rst-dynamic-block (dynamic-block contents info)
"Transcode a DYNAMIC-BLOCK element from Org to reStructuredText.
CONTENTS holds the contents of the block. INFO is a plist
holding contextual information."
contents)
;;;; Entity
(defun org-rst-entity (entity contents info)
"Transcode an ENTITY object from Org to reStructuredText.
CONTENTS are the definition itself. INFO is a plist holding
contextual information."
(let ((ent (org-element-property :latex entity)))
(if (org-element-property :latex-math-p entity)
(format ":math:`%s`" ent)
(org-element-property :utf-8 entity))))
;;;; Example Block
(defun org-rst-example-block (example-block contents info)
"Transcode an EXAMPLE-BLOCK element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual
information."
(let* ((example (org-remove-indentation
(org-element-property :value example-block)))
(label (org-element-property :name example-block))
(attributes
(org-export-read-attribute :attr_rst example-block))
(class (plist-get attributes :class)))
(when example
(concat
"::\n"
(when class (format " :class: %s\n" class))
(when label (format " :name: %s\n" label))
"\n"
(org-rst--indent-string example org-rst-quote-margin)))))
;;;; Export Block
(defun org-rst-export-block (export-block contents info)
"Transcode a EXPORT-BLOCK element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information."
(when (member (org-element-property :type export-block) '("RST" "REST" "RESTRUCTUREDTEXT"))
(org-element-property :value export-block)))
;;;; Export Snippet
(defun org-rst-export-snippet (export-snippet contents info)
"Transcode a EXPORT-SNIPPET object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information."
(when (eq (org-export-snippet-backend export-snippet) 'rst)
(org-element-property :value export-snippet)))
;;;; Fixed Width
(defun org-rst-fixed-width (fixed-width contents info)
"Transcode a FIXED-WIDTH element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist used as a communication
channel."
(org-rst--text-markup (org-element-property :value fixed-width) 'verbatim))
;;;; Footnote Definition
;(defun org-rst-footnote-definition (footnote-definition contents info)
; "Transcode a FOOTNOTE-DEFINITION element from Org to reStructuredText.
;CONTENTS is nil. INFO is a plist holding contextual information."
; (replace-regexp-in-string
; "^" ".. "
; (org-remove-indentation
; (org-element-property :value footnote-definition))))
;;;; Footnote Reference
(defun org-rst-footnote-reference (footnote-reference contents info)
"Transcode a FOOTNOTE-REFERENCE element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information."
(format " [%s]_ " (org-export-get-footnote-number footnote-reference info)))
;;;; Headline
(defun org-rst-headline (headline contents info)
"Transcode a HEADLINE element from Org to reStructuredText.
CONTENTS holds the contents of the headline. INFO is a plist
holding contextual information."
;; Don't export footnote section, which will be handled at the end
;; of the template.
(unless (org-element-property :footnote-section-p headline)
(let* (;; Blank lines between headline and its contents.
;; `org-rst-headline-spacing', when set, overwrites
;; original buffer's spacing.
(pre-blanks
(make-string
(if org-rst-headline-spacing (car org-rst-headline-spacing)
(org-element-property :pre-blank headline)) ?\n))
(customid (org-element-property :CUSTOM_ID headline))
(label (when customid
(format ".. _%s:\n\n" customid))))
(concat
(or label "")
(org-rst--build-title headline info 'underline)
"\n" pre-blanks
contents))))
;;;; Horizontal Rule
(defun org-rst-horizontal-rule (horizontal-rule contents info)
"Transcode an HORIZONTAL-RULE object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information."
"\n------------\n")
;;;; Inline Src Block
(defun org-rst-inline-src-block (inline-src-block contents info)
"Transcode an INLINE-SRC-BLOCK element from Org to reStructuredText.
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(org-rst--text-markup
(org-element-property :value inline-src-block) 'verbatim))
;;;; Inlinetask
(defun org-rst-inlinetask (inlinetask contents info)
"Transcode an INLINETASK element from Org to reStructuredText.
CONTENTS holds the contents of the block. INFO is a plist
holding contextual information."
(let ((title (org-export-data (org-element-property :title inlinetask) info))
(todo (and (plist-get info :with-todo-keywords)
(let ((todo (org-element-property :todo-keyword inlinetask)))
(and todo (org-export-data todo info)))))
(todo-type (org-element-property :todo-type inlinetask))
(tags (and (plist-get info :with-tags)
(org-export-get-tags inlinetask info)))
(priority (and (plist-get info :with-priority)
(org-element-property :priority inlinetask))))
;; If `org-rst-format-inlinetask-function' is provided, call it
;; with appropriate arguments.
(if (functionp org-rst-format-inlinetask-function)
(funcall org-rst-format-inlinetask-function
todo todo-type priority title tags contents)
;; Otherwise, use a default template.
(let ((full-title
(concat
(when todo (format "%s" todo))
(when priority (format "\#%c " priority))
title
(when tags (format ":%s:"
(mapconcat 'identity tags ":"))))))
(format (concat "%s\n\n"
"%s\n")
full-title contents)))))
;;;; Inner template
(defun org-rst-inner-template (contents info)
"Return complete document string after reStructuredText conversion.
CONTENTS is the transcoded contents string. INFO is a plist
holding export options."
(org-element-normalize-string
(concat
;; 1. Document's body.
contents
;; 2. Footnote definitions.
(let ((definitions (org-export-collect-footnote-definitions info))
;; Insert full links right inside the footnote definition
;; as they have no chance to be inserted later.
(org-rst-links-to-notes nil))
(when definitions
(concat
"\n\n"
(mapconcat
(lambda (ref)
(let ((id (format ".. [%s] " (car ref))))
;; Distinguish between inline definitions and
;; full-fledged definitions.
(org-trim
(let ((def (nth 2 ref)))
(if (eq (org-element-type def) 'org-data)
;; Full-fledged definition: footnote ID is
;; inserted inside the first parsed paragraph
;; (FIRST), if any, to be sure filling will
;; take it into consideration.
(let ((first (car (org-element-contents def))))
(if (not (eq (org-element-type first) 'paragraph))
(concat id "\n" (org-export-data def info))
(push id (nthcdr 2 first))
(org-export-data def info)))
;; Fill paragraph once footnote ID is inserted
;; in order to have a correct length for first
;; line.
(concat id (org-export-data def info)))))))
definitions "\n\n")))))))
;;;; Italic
(defun org-rst-italic (italic contents info)
"Transcode ITALIC from Org to reStructuredText.
CONTENTS is the text with italic markup. INFO is a plist holding
contextual information."
(org-rst--text-markup contents 'italic))
;;;; Item
(defun org-rst-item (item contents info)
"Transcode ITEM element into reStructuredText format.
CONTENTS is the item contents. INFO is a plist used as
a communication channel."
(let* ((checkbox (org-rst--checkbox item info))
(list-type (org-element-property :type (org-export-get-parent item)))
(tag (let
((tag (org-element-property :tag item)))
(and tag (concat (org-export-data tag info) checkbox))))
(bullet
;; First parent of ITEM is always the plain-list. Get
;; `:type' property from it.
(org-list-bullet-string
(cond
((eq list-type 'ordered)
;; Return correct number for ITEM, paying attention to
;; counters.
(let* ((struct (org-element-property :structure item))
(bul (org-element-property :bullet item))
(num (number-to-string
(car (last (org-list-get-item-number
(org-element-property :begin item)
struct
(org-list-prevs-alist struct)
(org-list-parents-alist struct)))))))
(replace-regexp-in-string "[0-9]+" num bul)))
(tag "")
(t "-"))))
(width (if tag 4 (string-width bullet)))
)
(concat
(if tag tag (concat bullet checkbox))
(let ((contents (org-rst--indent-string contents width)))
(if (and (not tag)
(eq (org-element-type (car (org-element-contents item))) 'paragraph))
(org-trim contents)
(concat "\n" contents))))))
;;;; Keyword
(defun org-rst-keyword (keyword contents info)
"Transcode a KEYWORD element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information."
(let ((key (org-element-property :key keyword))
(value (org-element-property :value keyword)))
(cond
((string= key "RST") value)
((string= key "TOC") (downcase value)))))
;;;; Latex Environment
(defun org-rst-latex-environment (latex-environment contents info)
"Transcode a LATEX-ENVIRONMENT element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual
information."
(when (plist-get info :with-latex)
(org-remove-indentation (org-element-property :value latex-environment))))
;;;; Latex Fragment
(defun org-rst-latex-fragment (latex-fragment contents info)
"Transcode a LATEX-FRAGMENT object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual
information."
(let ((value (org-element-property :value latex-fragment)))
(cond
((string-match "\\`\\(\\$\\{2\\}\\)\\([^\000]*\\)\\1\\'" value)
(format ".. math::\n\n%s"
(org-rst--indent-string
(org-trim (match-string 2 value)) org-rst-quote-margin)))
((string-match "\\`\\(\\$\\{1\\}\\)\\([^\000]*\\)\\1\\'" value)
(format ":math:`%s`" (org-trim (match-string 2 value))))
((string-match "\\`\\\\(\\([^\000]*\\)\\\\)\\'" value)
(format ":math:`%s`" (org-trim (match-string 1 value))))
((string-match "\\`\\\\\\[\\([^\000]*\\)\\\\\\]\\'" value)
(format "\.. math::\n\n%s"
(org-rst--indent-string
(org-trim (match-string 1 value)) org-rst-quote-margin)))
(t value))))
;;;; Line Break
(defun org-rst-line-break (line-break contents info)
"Transcode a LINE-BREAK object from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual
information."
hard-newline)
;;;; Link
(defun org-rst-inline-image-p (link info)
"Non-nil when LINK is meant to appear as an image.
INFO is a plist used as a communication channel. LINK is an
inline image when it has no description and targets an image
file (see `org-rst-inline-image-rules' for more information), or
if its description is a single link targeting an image file."
(if (not (org-element-contents link))
(org-export-inline-image-p
link (plist-get info :rst-inline-image-rules))
(not
(let ((link-count 0))
(org-element-map (org-element-contents link)
(cons 'plain-text org-element-all-objects)
(lambda (obj)
(case (org-element-type obj)
(plain-text (org-string-nw-p obj))
(link (if (= link-count 1) t
(incf link-count)
(not (org-export-inline-image-p
obj (plist-get info :rst-inline-image-rules)))))
(otherwise t)))
info t)))))
(defun my-org-export-inline-image-p (link &optional rules)
(let ((case-fold-search t)
(rules (or rules org-export-default-inline-image-rule)))
(catch 'exit
(mapc
(lambda (rule)
(if (string-match (cdr rule) link)
(throw 'exit t)))
rules)
;; Return nil if no rule matched.
nil)))
(defun org-rst-link (link desc info)
"Transcode a LINK object from Org to reStructuredText.
DESC is the description part of the link, or the empty string.
INFO is a plist holding contextual information."
(let* ((home (when (plist-get info :html-link-home)
(org-trim (plist-get info :html-link-home))))
(use-abs-url (plist-get info :html-link-use-abs-url))
(link-org-files-as-html-maybe
(function
(lambda (raw-path info)
"Treat links to `file.org' as links to `file.html', if needed.
See `org-rst-link-org-files-as-html'."
(cond
((and (plist-get info :rst-link-org-files-as-html)
(string= ".org"
(downcase (file-name-extension raw-path "."))))
(concat (file-name-sans-extension raw-path) "."
(plist-get info :html-extension)))
(t raw-path)))))
(type (org-element-property :type link))
(raw-path (org-element-property :path link))
;; Ensure DESC really exists, or set it to nil.
(desc (and (not (string= desc "")) desc))
(path (cond
((member type '("http" "https" "ftp" "mailto"))
(org-link-escape
(org-link-unescape
(concat type ":" raw-path)) org-link-escape-chars-browser))
((string= type "file")
;; Treat links to ".org" files as ".html", if needed.
(setq raw-path
(funcall link-org-files-as-html-maybe raw-path info))
(cond ((and home use-abs-url)
(setq raw-path
(concat (file-name-as-directory home) raw-path)))
(t raw-path)))
(t raw-path)))
;; Extract attributes from parent's paragraph. HACK: Only do
;; this for the first link in parent (inner image link for
;; inline images). This is needed as long as attributes
;; cannot be set on a per link basis.
(attributes-plist
(let* ((parent (org-export-get-parent-element link))
(link (let ((container (org-export-get-parent link)))
(if (and (eq (org-element-type container) 'link)
(org-rst-inline-image-p link info))
container
link))))