1use std::borrow::Cow;
2use std::fmt;
3use std::fmt::Write;
4
5use crate::ExportError;
6use crate::include::include_handle;
7use crate::org_macros::macro_handle;
8use crate::types::{ConfigOptions, Exporter, ExporterInner, LogicErrorKind};
9use org_parser::element::{Block, BulletKind, CounterKind, Priority, TableRow, Tag};
10use org_parser::object::{LatexFragment, PlainOrRec};
11
12use org_parser::{Expr, NodeID, Parser, parse_org};
13
14pub struct Org<'buf> {
22 buf: &'buf mut dyn fmt::Write,
23 indentation_level: u8,
24 on_newline: bool,
25 conf: ConfigOptions,
26 errors: Vec<ExportError>,
27}
28
29macro_rules! w {
30 ($dst:expr, $($arg:tt)*) => {
31 $dst.write_fmt(format_args!($($arg)*)).expect("writing to buffer during export failed")
32 };
33}
34
35impl<'buf> Exporter<'buf> for Org<'buf> {
36 fn export(input: &str, conf: ConfigOptions) -> core::result::Result<String, Vec<ExportError>> {
37 let mut buf = String::new();
38 Org::export_buf(input, &mut buf, conf)?;
39 Ok(buf)
40 }
41
42 fn export_buf<'inp, T: fmt::Write>(
43 input: &'inp str,
44 buf: &'buf mut T,
45 conf: ConfigOptions,
46 ) -> core::result::Result<(), Vec<ExportError>> {
47 let parsed = parse_org(input);
48 Org::export_tree(&parsed, buf, conf)
49 }
50
51 fn export_tree<'inp, T: fmt::Write>(
52 parsed: &Parser,
53 buf: &'buf mut T,
54 conf: ConfigOptions,
55 ) -> core::result::Result<(), Vec<ExportError>> {
56 let mut obj = Org {
57 buf,
58 indentation_level: 0,
59 on_newline: false,
60 conf,
61 errors: Vec::new(),
62 };
63
64 obj.export_rec(&parsed.pool.root_id(), parsed);
65
66 if obj.errors().is_empty() {
67 Ok(())
68 } else {
69 Err(obj.errors)
70 }
71 }
72}
73
74impl<'buf> ExporterInner<'buf> for Org<'buf> {
75 fn export_macro_buf<'inp, T: fmt::Write>(
76 input: &'inp str,
77 buf: &'buf mut T,
78 _conf: ConfigOptions,
79 ) -> core::result::Result<(), Vec<ExportError>> {
80 let parsed = org_parser::parse_macro_call(input);
81
82 let mut obj = Org {
83 buf,
84 indentation_level: 0,
85 on_newline: false,
86 conf: ConfigOptions::default(),
87 errors: Vec::new(),
88 };
89
90 obj.export_rec(&parsed.pool.root_id(), &parsed);
91 if obj.errors().is_empty() {
92 Ok(())
93 } else {
94 Err(obj.errors)
95 }
96 }
97
98 fn export_rec(&mut self, node_id: &NodeID, parser: &Parser) {
99 let node = &parser.pool[*node_id];
100 match &node.obj {
101 Expr::Root(inner) => {
102 for id in inner {
103 self.export_rec(id, parser);
104 }
105 }
106 Expr::Heading(inner) => {
107 for _ in 0..inner.heading_level.into() {
108 w!(self, "*");
109 }
110 w!(self, " ");
111
112 if let Some(keyword) = inner.keyword {
113 w!(self, "{keyword} ");
114 }
115
116 if let Some(priority) = &inner.priority {
117 w!(self, "[#");
118 match priority {
119 Priority::A => w!(self, "A"),
120 Priority::B => w!(self, "B"),
121 Priority::C => w!(self, "C"),
122 Priority::Num(num) => w!(self, "{num}"),
123 };
124 w!(self, "] ");
125 }
126
127 if let Some(title) = &inner.title {
128 for id in &title.1 {
129 self.export_rec(id, parser);
130 }
131 }
132
133 if let Some(tags) = &inner.tags {
150 let mut valid_out = String::new();
151 for tag in tags.iter().rev() {
152 match tag {
153 Tag::Raw(val) => w!(&mut valid_out, ":{val}"),
154 Tag::Loc(_id) => {
155 }
157 }
158 }
159 if !valid_out.is_empty() {
161 w!(self, " {valid_out}:");
162 }
163 }
164
165 w!(self, "\n");
166
167 if let Some(children) = &inner.children {
168 for id in children {
169 self.export_rec(id, parser);
170 }
171 }
172 }
173 Expr::Block(inner) => {
174 match inner {
175 Block::Center {
177 parameters,
178 contents,
179 } => {
180 w!(self, "#+begin_center");
181 for (key, val) in parameters {
182 w!(self, " :{} {}", key, val);
183 }
184 w!(self, "\n");
185 for id in contents {
186 self.export_rec(id, parser);
187 }
188 w!(self, "#+end_center\n");
189 }
190 Block::Quote {
191 parameters,
192 contents,
193 } => {
194 w!(self, "#+begin_quote");
195 for (key, val) in parameters {
196 w!(self, " :{} {}", key, val);
197 }
198 w!(self, "\n");
199 for id in contents {
200 self.export_rec(id, parser);
201 }
202 w!(self, "#+end_quote\n");
203 }
204 Block::Special {
205 parameters,
206 contents,
207 name,
208 } => {
209 w!(self, "#+begin_{name}");
210 for (key, val) in parameters {
211 w!(self, " :{} {}", key, val);
212 }
213 w!(self, "\n");
214 for id in contents {
215 self.export_rec(id, parser);
216 }
217 w!(self, "#+end_{name}\n");
218 }
219
220 Block::Comment {
222 parameters,
223 contents,
224 } => {
225 w!(self, "#+begin_comment");
226 for (key, val) in parameters {
227 w!(self, " :{} {}", key, val);
228 }
229 w!(self, "\n{contents}");
230 w!(self, "#+end_comment\n");
231 }
232 Block::Example {
233 parameters,
234 contents,
235 } => {
236 w!(self, "#+begin_example");
237 for (key, val) in parameters {
238 w!(self, " :{} {}", key, val);
239 }
240 w!(self, "\n{contents}");
241 w!(self, "#+end_example\n");
242 }
243 Block::Export {
244 backend,
245 parameters,
246 contents,
247 } => {
248 let back = if let Some(word) = backend { word } else { "" };
249 w!(self, "#+begin_export {}", back);
250 for (key, val) in parameters {
251 w!(self, " :{} {}", key, val);
252 }
253 w!(self, "\n{contents}");
254 w!(self, "#+end_export\n");
255 }
256 Block::Src {
257 language,
258 parameters,
259 contents,
260 } => {
261 let lang = if let Some(word) = language { word } else { "" };
262 w!(self, "#+begin_src {}", lang);
263 for (key, val) in parameters {
264 w!(self, " :{} {}", key, val);
265 }
266 w!(self, "\n{contents}");
267 w!(self, "#+end_src\n");
268 }
269 Block::Verse {
270 parameters,
271 contents,
272 } => {
273 w!(self, "#+begin_verse");
274 for (key, val) in parameters {
275 w!(self, " :{} {}", key, val);
276 }
277 w!(self, "\n{contents}");
278 w!(self, "#+end_verse\n");
279 }
280 }
281 }
282 Expr::RegularLink(inner) => {
283 w!(self, "[");
284 w!(self, "[{}]", inner.path.obj);
285 if let Some(children) = &inner.description {
286 w!(self, "[");
287 for id in children {
288 self.export_rec(id, parser);
289 }
290 w!(self, "]");
291 }
292 w!(self, "]");
293 }
294
295 Expr::Paragraph(inner) => {
296 for id in &inner.0 {
297 self.export_rec(id, parser);
298 }
299 w!(self, "\n");
300 }
301
302 Expr::Italic(inner) => {
303 w!(self, "/");
304 for id in &inner.0 {
305 self.export_rec(id, parser);
306 }
307 w!(self, "/");
308 }
309 Expr::Bold(inner) => {
310 w!(self, "*");
311 for id in &inner.0 {
312 self.export_rec(id, parser);
313 }
314 w!(self, "*");
315 }
316 Expr::StrikeThrough(inner) => {
317 w!(self, "+");
318 for id in &inner.0 {
319 self.export_rec(id, parser);
320 }
321 w!(self, "+");
322 }
323 Expr::Underline(inner) => {
324 w!(self, "_");
325 for id in &inner.0 {
326 self.export_rec(id, parser);
327 }
328 w!(self, "_");
329 }
330 Expr::BlankLine => {
331 w!(self, "\n");
332 }
333 Expr::SoftBreak => {
334 w!(self, " ");
335 }
336 Expr::LineBreak => {
337 w!(self, r#"\\"#);
338 }
339 Expr::HorizontalRule => {
340 w!(self, "-----\n");
341 }
342 Expr::Plain(inner) => {
343 w!(self, "{inner}");
344 }
345 Expr::Verbatim(inner) => {
346 w!(self, "={}=", inner.0);
347 }
348 Expr::Code(inner) => {
349 w!(self, "~{}~", inner.0);
350 }
351 Expr::Comment(inner) => {
352 w!(self, "# {}\n", inner.0);
353 }
354 Expr::InlineSrc(inner) => {
355 w!(self, "src_{}", inner.lang);
356 if let Some(args) = inner.headers {
357 w!(self, "[{args}]");
358 }
359 w!(self, "{{{}}}", inner.body);
360 }
361 Expr::Keyword(inner) => {
362 if inner.key.eq_ignore_ascii_case("include")
363 && let Err(e) = include_handle(inner.val, self)
364 {
365 self.errors().push(ExportError::LogicError {
366 span: node.start..node.end,
367 source: LogicErrorKind::Include(e),
368 });
369 }
370 }
371 Expr::LatexEnv(inner) => {
372 w!(
373 self,
374 r"\begin{{{0}}}
375{1}
376\end{{{0}}}
377",
378 inner.name,
379 inner.contents
380 );
381 }
382 Expr::LatexFragment(inner) => match inner {
383 LatexFragment::Command { name, contents } => {
384 w!(self, r#"\{name}"#);
385 if let Some(command_cont) = contents {
386 w!(self, "{{{command_cont}}}");
387 }
388 }
389 LatexFragment::Display(inner) => {
390 w!(self, r"\[{inner}\]");
391 }
392 LatexFragment::Inline(inner) => {
393 w!(self, r#"\({inner}\)"#);
394 }
395 },
396 Expr::Item(inner) => {
397 match inner.bullet {
398 BulletKind::Unordered => {
399 w!(self, "-");
400 }
401 BulletKind::Ordered(counterkind) => match counterkind {
402 CounterKind::Letter(lettre) => {
403 w!(self, "{}.", lettre as char);
404 }
405 CounterKind::Number(num) => {
406 w!(self, "{num}.");
407 }
408 },
409 }
410 w!(self, " ");
411
412 if let Some(counter_set) = inner.counter_set {
413 w!(self, "[@{counter_set}]");
414 }
415
416 if let Some(check) = &inner.check_box {
417 let val: &str = check.into();
418 w!(self, "[{val}] ");
419 }
420
421 if let Some(tag) = inner.tag {
422 w!(self, "{tag} :: ");
423 }
424
425 self.indentation_level += 1;
426 for id in &inner.children {
427 self.export_rec(id, parser);
428 }
429 self.indentation_level -= 1;
430 if self.indentation_level == 0 {
431 self.on_newline = false;
432 }
433 }
434 Expr::PlainList(inner) => {
435 for id in &inner.children {
436 self.export_rec(id, parser);
437 }
438 }
439 Expr::PlainLink(inner) => {
440 w!(self, "[[{}:{}]]", inner.protocol, inner.path);
441 }
442 Expr::Entity(inner) => {
443 w!(self, "{}", inner.mapped_item);
444 }
445 Expr::Table(inner) => {
446 let mut build_vec: Vec<Vec<String>> = Vec::with_capacity(inner.rows);
447 for _ in 0..self.indentation_level {
451 self.buf.write_str(" ").unwrap();
453 }
454 self.on_newline = false;
455
456 for id in &inner.children {
458 match &parser.pool[*id].obj {
459 Expr::TableRow(row) => {
460 let mut row_vec = vec![];
461 match &row {
462 TableRow::Standard(stans) => {
463 for id in stans {
464 let mut cell_buf = String::new();
465 let mut new_obj = Org {
467 buf: &mut cell_buf,
468 indentation_level: self.indentation_level,
469 on_newline: self.on_newline,
470 conf: self.conf.clone(),
471 errors: Vec::new(),
472 };
473 new_obj.export_rec(id, parser);
474 row_vec.push(cell_buf);
475 }
476 }
477 TableRow::Rule => {
478 }
480 }
481 build_vec.push(row_vec);
482 }
483 _ => unreachable!(),
484 }
485 }
486
487 let mut col_widths = Vec::with_capacity(inner.cols);
494 for col_ind in 0..inner.cols {
495 let mut curr_max = 0;
496 for row in &build_vec {
497 curr_max = curr_max.max(row.get(col_ind).map_or_else(|| 0, |v| v.len()));
498 }
499 col_widths.push(curr_max);
500 }
501
502 for row in &build_vec {
503 w!(self, "|");
504
505 if row.is_empty() {
507 for (i, val) in col_widths.iter().enumerate() {
508 for _ in 0..(*val + 2) {
510 w!(self, "-");
511 }
512
513 if i == inner.cols {
514 w!(self, "|");
515 } else {
516 w!(self, "+");
517 }
518 }
519 } else {
520 for (col_ind, col_width) in col_widths.iter().enumerate() {
521 let cell = row.get(col_ind);
522 let diff;
523
524 w!(self, " ");
526 if let Some(strang) = cell {
527 diff = col_width - strang.len();
528 w!(self, "{strang}");
529 } else {
530 diff = *col_width;
531 };
532
533 for _ in 0..diff {
534 w!(self, " ");
535 }
536
537 w!(self, " |");
539 }
540 }
541 w!(self, "\n");
542 }
543 }
544
545 Expr::TableRow(_) => {
546 unreachable!("handled by Expr::Table")
547 }
548 Expr::TableCell(inner) => {
549 for id in &inner.0 {
550 self.export_rec(id, parser);
551 }
552 }
553 Expr::Emoji(inner) => {
554 w!(self, "{}", inner.mapped_item);
555 }
556 Expr::Superscript(inner) => match &inner.0 {
557 PlainOrRec::Plain(inner) => {
558 w!(self, "^{{{inner}}}");
559 }
560 PlainOrRec::Rec(inner) => {
561 w!(self, "^{{");
562 for id in inner {
563 self.export_rec(id, parser);
564 }
565
566 w!(self, "}}");
567 }
568 },
569 Expr::Subscript(inner) => match &inner.0 {
570 PlainOrRec::Plain(inner) => {
571 w!(self, "_{{{inner}}}");
572 }
573 PlainOrRec::Rec(inner) => {
574 w!(self, "_{{");
575 for id in inner {
576 self.export_rec(id, parser);
577 }
578
579 w!(self, "}}");
580 }
581 },
582 Expr::Target(inner) => {
583 w!(self, "<<{}>>", inner.0);
584 }
585 Expr::Macro(macro_call) => {
586 let macro_contents = match macro_handle(parser, macro_call, self.config_opts()) {
587 Ok(contents) => contents,
588 Err(e) => {
589 self.errors().push(ExportError::LogicError {
590 span: node.start..node.end,
591 source: LogicErrorKind::Macro(e),
592 });
593 return;
594 }
595 };
596
597 match macro_contents {
598 Cow::Owned(p) => {
599 if let Err(mut err_vec) =
600 Org::export_macro_buf(&p, self, self.config_opts().clone())
601 {
602 self.errors().append(&mut err_vec);
603 }
604 }
605 Cow::Borrowed(r) => {
606 w!(self, "{r}");
607 }
608 }
609 }
610 Expr::Drawer(inner) => {
611 w!(self, ":{}:\n", inner.name);
612 for id in &inner.children {
613 self.export_rec(id, parser);
614 }
615 w!(self, ":end:\n");
616 }
617 Expr::ExportSnippet(inner) => {
618 if inner.backend == "org" {
619 w!(self, "{}", inner.contents);
620 }
621 }
622 Expr::Affiliated(_) => {}
623 Expr::MacroDef(_) => {}
624 Expr::FootnoteDef(inner) => {
625 w!(self, r"[fn:{}] ", inner.label);
626
627 for id in &inner.children {
628 self.export_rec(id, parser);
629 }
630 }
631 Expr::FootnoteRef(inner) => {
632 w!(self, r"[fn:");
633 if let Some(label) = inner.label {
634 w!(self, "{label}");
635 }
636 if let Some(descr) = &inner.children {
637 w!(self, ":");
638 for id in descr {
639 self.export_rec(id, parser);
640 }
641 }
642 w!(self, "]");
643 }
644 }
645 }
646
647 fn backend_name() -> &'static str {
648 "org"
649 }
650
651 fn config_opts(&self) -> &ConfigOptions {
652 &self.conf
653 }
654
655 fn errors(&mut self) -> &mut Vec<ExportError> {
656 &mut self.errors
657 }
658}
659
660impl<'buf> fmt::Write for Org<'buf> {
661 fn write_str(&mut self, s: &str) -> fmt::Result {
662 if self.indentation_level > 0 {
663 for chunk in s.split_inclusive('\n') {
664 if self.on_newline {
665 for _ in 0..self.indentation_level {
666 self.buf.write_str(" ")?;
667 }
668 }
669 self.on_newline = chunk.ends_with('\n');
670 self.buf.write_str(s)?;
671 }
672
673 Ok(())
678 } else {
679 self.buf.write_str(s)
680 }
681 }
682}
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687
688 use pretty_assertions::assert_eq;
689
690 fn org_export(input: &str) -> String {
691 Org::export(input, ConfigOptions::default()).unwrap()
692 }
693
694 #[test]
695 fn basic_org_export() {
696 let out_str = org_export(
697 r"** one two
698three
699*four*
700
701",
702 );
703
704 assert_eq!(
705 out_str,
706 r"** one two
707three *four*
708
709"
710 );
711 }
712
713 #[test]
714 fn fancy_list_export() {
715 let a = org_export(
716 r"
717 + one two three
718 four five six
719
720 + two
721 + three
722 + four
723 +five
724",
725 );
726
727 assert_eq!(
728 a,
729 r"
730- one two three
731four five six
732
733- two
734- three
735- four
736+five
737"
738 );
739 }
740
741 #[test]
742 fn test_link_export() {
743 let out = org_export("[[https://swag.org][meowww]]");
744 println!("{out}");
745 }
746
747 #[test]
748 fn test_beeg() {
749 let out = org_export(
750 r"* DONE [#0] *one* two /three/ /four* :one:two:three:four:
751more content here this is a pargraph
752** [#1] descendant headline :five:
753*** [#2] inherit the tags
754** [#3] different level
755subcontent
756this
757more content here this is a pargraph
758** [#1] descendant headline :five:
759*** [#2] inherit the tags
760** [#3] different level
761subcontent
762this
763
764is a different paragraph
765id) =
766more subcontent
767
768* [#4] separate andy
769more content here this is a pargraph
770more content here this is a pargraph
771more content here this is a pargraph
772more content here this is a pargraph
773more content here this is a pargraph
774more content here this is a pargraph
775more content here this is a pargraph
776more content here this is a pargraph
777more content here this is a pargraph
778more content here this is a pargraph
779more content here this is a pargraph
780more content here this is a pargraph
781more content here this is a pargraph
782more content here this is a pargraph
783more content here this is a pargraph
784
785is a different paragraph
786id) =
787more subcontent
788
789* [#4] separate andy
790more content here this is a pargraph
791more content here this is a pargraph
792more content here this is a pargraph
793more content here this is a pargraph
794more content here this is a pargraph
795** [#1] descendant headline :five:
796*** [#2] inherit the tags
797** [#3] different level
798subcontent
799this
800
801is a different paragraph
802id) =
803more subcontent
804
805* [#4] separate andy
806more content here this is a pargraph
807more content here this is a pargraph
808more content here this is a pargraph
809more content here this is a pargraph
810more content here this is a pargraph
811more content here this is a pargraph
812more content here this is a pargraph
813more content here this is a pargraph
814more content here this is a pargraph
815more content here this is a pargraph
816more content here this is a pargraph
817more content here this is a pargraph
818more content here this is a pargraph
819more content here this is a pargraph
820more content here this is a pargraph
821more content here this is a pargraph
822more content here this is a pargraph
823more content here this is a pargraph
824more content here this is a pargraph
825more content here this is a pargraph
826more content here this is a pargraph
827more content here this is a pargraph
828more content here this is a pargraph
829more content here this is a pargraph
830** [#1] descendant headline :five:
831*** [#2] inherit the tags
832** [#3] different level
833subcontent
834this
835
836is a different paragraph
837id) =
838more subcontent
839
840* [#4] separate andy
841more content here this is a pargraph
842more content here this is a pargraph
843more content here this is a pargraph
844more content here this is a pargraph
845more content here this is a pargraph
846more content here this is a pargraph
847more content here this is a pargraph
848more content here this is a pargraph
849more content here this is a pargraph
850more content here this is a pargraph
851more content here this is a pargraph
852more content here this is a pargraph
853more content here this is a pargraph
854more content here this is a pargraph
855more content here this is a pargraph
856** a
857more content here this is a pargraph
858more content here this is a pargraph
859more content here this is a pargraph
860more content here this is a pargraph
861more content here this is a pargraph
862more content here this is a pargraph
863more content here this is a pargraph
864more content here this is a pargraph
865more content here this is a pargraph
866more content here this is a pargraph
867more content here this is a pargraph
868more content here this is a pargraph
869* a
870more content here this is a pargraph
871more content here this is a pargraph
872more content here this is a pargraph
873more content here this is a pargraph
874more content here this is a pargraph
875more content here this is a pargraph
876more content here this is a pargraph
877more content here this is a pargraph
878more content here this is a pargraph
879more content here this is a pargraph
880",
881 );
882
883 println!("{out}");
884 }
885
886 #[test]
887 fn less() {
888 let out = org_export(
889 r"* [#1] abc :c:
890** [#1] descendant headline :a:b:
891*** [#2] inherit the tags
892** [#3] different level
893",
894 );
895
896 assert_eq!(
897 out,
898 r"* [#1] abc :c:
899** [#1] descendant headline :a:b:
900*** [#2] inherit the tags
901** [#3] different level
902"
903 );
904 println!("{out}");
905 }
906
907 #[test]
908 fn list_export() {
909 let a = org_export(
910 r"
911- one
912 - two
913 - three
914 - four
915 - five
916- six
917 - seven
918",
919 );
920
921 println!("{a}");
922 assert_eq!(
923 a,
924 r"
925- one
926 - two
927 - three
928 - four
929 - five
930- six
931 - seven
932"
933 );
934 }
935
936 #[test]
937 fn basic_list_export() {
938 let a = org_export(
939 r"
940- one
941 - two
942- three
943 - four
944- five
945 - six
946 - seven
947- eight
948",
949 );
950
951 println!("{a}");
952 assert_eq!(
953 a,
954 r"
955- one
956 - two
957- three
958 - four
959- five
960 - six
961 - seven
962- eight
963"
964 );
965 }
966
967 #[test]
968 fn list_words() {
969 let a: String = org_export(
970 r"
9711. item 1
972 abcdef
973
974 next one two three four five
975
976 more thangs more thangs more thangs
977 more thangs
978
9792. [X] item 2
980 - aome tag :: item 2.1
981",
982 );
983
984 println!("{a}");
985
986 }
1004 #[test]
1005 fn table_export() {
1006 let a = org_export(
1007 r"
1008|one|two|
1009|three|four|
1010|five|six|seven|
1011|eight
1012",
1013 );
1014
1015 assert_eq!(
1016 a,
1017 r"
1018| one | two | |
1019| three | four | |
1020| five | six | seven |
1021| eight | | |
1022"
1023 );
1024 }
1025
1026 #[test]
1027 fn table_export_hrule() {
1028 let a = org_export(
1029 r"
1030|one|two|
1031|-
1032|three|four|
1033|five|six|seven|
1034|eight
1035|-
1036|swagg|long the
1037|okay| _underline_| ~fake| _fake|
1038",
1039 );
1040
1041 println!("{a}");
1042 assert_eq!(
1043 a,
1044 r"
1045| one | two | | |
1046|-------+--------------+--------+--------+
1047| three | four | | |
1048| five | six | seven | |
1049| eight | | | |
1050|-------+--------------+--------+--------+
1051| swagg | long the | | |
1052| okay | _underline_ | ~fake | _fake |
1053"
1054 );
1055 }
1057
1058 #[test]
1059 fn indented_table() {
1060 let a = org_export(
1061 r"
1062- zero
1063 |one|two|
1064 |-
1065 |three|four|
1066 |five|six|seven|
1067 |eight
1068 |-
1069 |swagg|long the
1070 |okay| _underline_| ~fake| _fake|
1071- ten
1072",
1073 );
1074
1075 assert_eq!(
1076 a,
1077 r"
1078- zero
1079 | one | two | | |
1080 |-------+--------------+--------+--------+
1081 | three | four | | |
1082 | five | six | seven | |
1083 | eight | | | |
1084 |-------+--------------+--------+--------+
1085 | swagg | long the | | |
1086 | okay | _underline_ | ~fake | _fake |
1087- ten
1088"
1089 );
1090 }
1091
1092 #[test]
1093 fn proper_list_indent() {
1094 let a = org_export(
1095 r"
1096- one
1097- four
1098 - one
1099 - two
1100",
1101 );
1102
1103 assert_eq!(
1104 a,
1105 r"
1106- one
1107- four
1108 - one
1109 - two
1110"
1111 );
1112 }
1113
1114 #[test]
1115 fn heading_list_not() {
1116 let a = org_export(
1117 r"
1118- one
1119- four
1120* one
1121",
1122 );
1123
1124 assert_eq!(
1128 a,
1129 r"
1130- one
1131- four
1132* one
1133"
1134 );
1135 }
1136
1137 #[test]
1138 fn proper_link() {
1139 let a = org_export(r"[[abc][one]]");
1140
1141 assert_eq!(
1142 a,
1143 r"[[abc][one]]
1144"
1145 );
1146 }
1147
1148 #[test]
1149 fn link_odd() {
1150 let a = org_export("[aayyyy][one]]");
1151 assert_eq!(
1152 a,
1153 r"[aayyyy][one]]
1154"
1155 );
1156 }
1157
1158 #[test]
1159 fn superscript() {
1160 let a = org_export(r"sample_text^{\gamma}");
1161 assert_eq!(
1162 a,
1163 r"sample_{text}^{γ}
1164"
1165 );
1166
1167 let b = org_export(
1168 r"sample_text^bunchoftextnowhite!,lkljas
1169 after",
1170 );
1171
1172 assert_eq!(
1173 b,
1174 r"sample_{text}^{bunchoftextnowhite}!,lkljas after
1175"
1176 );
1177
1178 let c = org_export(r"nowhere ^texto");
1179
1180 assert_eq!(
1181 c,
1182 r"nowhere ^texto
1183"
1184 );
1185 }
1186
1187 #[test]
1188 fn subscript() {
1189 let a = org_export(r"sample_text_{\gamma}");
1190 assert_eq!(
1191 a,
1192 r"sample_{text}_{γ}
1193"
1194 );
1195
1196 let b = org_export(
1197 r"sample_{text}_bunchoftextnowhite!,lkljas
1198 after",
1199 );
1200
1201 assert_eq!(
1202 b,
1203 r"sample_{text}_{bunchoftextnowhite}!,lkljas after
1204"
1205 );
1206
1207 let c = org_export(r"nowhere _texto");
1208
1209 assert_eq!(
1210 c,
1211 r"nowhere _texto
1212"
1213 );
1214 }
1215
1216 #[test]
1217 fn plain_link() {
1218 let a = org_export("https://cool.com abc rest");
1219
1220 assert_eq!(
1221 a,
1222 "[[https://cool.com]] abc rest
1223"
1224 );
1225 }
1226
1227 #[test]
1228 fn newline_literal_markup() {
1229 let a = org_export(
1230 r"- test =if ~literal $interpreters \[handle newline \(properly {{{in(a lists
1231- text that isn't disappearing!
1232",
1233 );
1234
1235 assert_eq!(
1236 a,
1237 r"- test =if ~literal $interpreters \[handle newline \(properly {{{in(a lists
1238- text that isn't disappearing!
1239"
1240 );
1241 }
1242
1243 #[test]
1244 fn lblock_plus_list() {
1245 let a = org_export(
1246 r"
1247-
1248 #+begin_src
1249
1250
1251hiiiiiiiiiiiiiiiiiii
1252
1253meowwwwwwwwww
1254 #+end_src
1255
1256-
1257",
1258 );
1259 println!("{a}");
1260 }
1261
1262 #[test]
1263 fn markup_enclosed_in_bracks() {
1264 let a = org_export(r"[_enclosed text here_]");
1265
1266 assert_eq!(
1267 a,
1268 "[_enclosed text here_]
1269"
1270 );
1271 }
1272
1273 #[test]
1274 fn drawer() {
1275 let a = org_export(
1276 r"
1277:NAME:
1278
1279*words*
1280
1281||||abcds|
1282
1283
1284
1285* abc one two three
1286
1287four
1288:end:
1289",
1290 );
1291 assert_eq!(
1292 a,
1293 r"
1294:NAME:
1295
1296*words*
1297
1298| | | | abcds |
1299
1300
1301
1302* abc one two three
1303
1304four
1305:end:
1306
1307"
1308 );
1309 }
1310}