Verse and text block support. Support for code block without language.
Multiline code blocks support. Better handling of broken list indents.
Detect and ignore drawer property. Generate proper links for most URLs.
Document property name shall not have whitespace.
Documented missing features.
// " * my list title"
tokenizer.addTerminator(PRESERVE, "([ \\t]+)(\\*)[ \\t]+(.*)?\\r?\\n", TG_LIST);
+ // TODO: add numbered list
+
// DocumentProperty:
// "#+OPTIONS: H:20 num:20"
- tokenizer.addTerminator(PRESERVE, "#\\+.+:.*\\r?\\n", TG_DOCUMENT_PROPERTY);
+ tokenizer.addTerminator(PRESERVE, "#\\+[^\\s]+:.*\\r?\\n", TG_DOCUMENT_PROPERTY);
- // newline
- tokenizer.addTerminator(PRESERVE,".*\\r?\\n", TG_NORMAL_TEXT);
+ // Drawer property:
+ // " :ID: 533734b9-0456-4448-9830-a43646345615"
+ tokenizer.addTerminator(PRESERVE, "([ \\t]*):([^\\s]+):(.*)\\r?\\n", TG_DRAWER_PROPERTY);
+
+
+ // multiline code block
+ tokenizer.addTerminator(PRESERVE,
+ "([ \\t]*)#\\+BEGIN_SRC" + // source begin identifier
+ "(([ \\t]+)(.*))?(\\r?\\n)" + // source block parameters
+ "((?:.|\\n|\\r)*?)" + // source content
+ "(\\r?\\n)([ \\t]*)#\\+END_SRC(.*)\\r?\\n" // source end identifier
+ , TG_MULTILINE_CODE);
+
+ // verse
+ tokenizer.addTerminator(PRESERVE,
+ "([ \\t]*)#\\+BEGIN_VERSE" + // verse begin identifier
+ "(([ \\t]+)(.*))?(\\r?\\n)" + // verse block parameters
+ "((?:.|\\n|\\r)*?)" + // verse
+ "(\\r?\\n)([ \\t]*)#\\+END_VERSE(.*)\\r?\\n" // verse end identifier
+ , TG_VERSE);
+ // TODO: add support for export blocks:
+ // #+begin_export latex
+ // \clearpage
+ //#+end_export
+
+
+ // normal text
+ tokenizer.addTerminator(PRESERVE,".*\\r?\\n", TG_NORMAL_TEXT);
+
while (tokenizer.hasMoreContent()) {
final TokenizerMatch tm = tokenizer.getNextToken();
return;
}
- if (indent == currentListElement.indent){
+ if (indent > currentListElement.parent.indent){
// list depth is the same
ListElement newElement = new ListElement(title, indent, currentListElement.parent, type);
currentListElement.parent.addContent(newElement);
// list dept decreases
while (true){
if (currentListElement.parent.indent <= indent){
- if (currentListElement.parent.indent < 0){
- // reached first depth level, cannot go any deeper.
- // This special situation arisesbb only when lint indents are not properly aligned.
- // That is, document structure is incorrect.
- ListElement newElement = new ListElement(title, indent, currentListElement.parent, type);
- currentListElement.parent.addContent(newElement);
- currentListElement = newElement;
- return;
- }
-
ListElement newElement = new ListElement(title, indent, currentListElement.parent.parent, type);
currentListElement.parent.parent.addContent(newElement);
currentListElement = newElement;
public class Helper {
public static final String TG_NORMAL_TEXT = "normaltext";
+ public static final String TG_MULTILINE_CODE = "multiline code";
+ public static final String TG_VERSE = "verse";
public static final String TG_HYPERLINK = "hyperlink";
public static final String TG_HEADING = "heading";
public static final String TG_LIST = "list";
- public static final String TG_DOCUMENT_PROPERTY = "document property";
+ public static final String TG_DOCUMENT_PROPERTY = "document property";
+ public static final String TG_DRAWER_PROPERTY = "drawer property";
}
--- /dev/null
+package eu.svjatoslav.sixth.core.document;
+
+import eu.svjatoslav.commons.string.String2;
+
+public class Utils {
+
+ public static String addIndentExceptFirstLine(String input, int indent) {
+ String[] lines = input.split("\\r?\\n");
+
+ StringBuilder sb = new StringBuilder();
+
+ if (lines.length >0 ) sb.append(lines[0]);
+
+ for (int i = 1; i< lines.length; i++) {
+ sb.append("\n");
+ sb.append(new String2(" ").repeat(indent).toString());
+ sb.append(lines[i]);
+ }
+
+ return sb.toString();
+ }
+
+}
import java.util.ArrayList;
import java.util.List;
-import static eu.svjatoslav.sixth.core.document.Helper.TG_DOCUMENT_PROPERTY;
-import static eu.svjatoslav.sixth.core.document.Helper.TG_NORMAL_TEXT;
+import static eu.svjatoslav.sixth.core.document.Helper.*;
public class ListElement implements Content {
public final FormattedText name;
public final ListElement parent;
private final String type;
private final List<Content> content = new ArrayList<>();
+ StringBuilder normalTextAccumulator = new StringBuilder();
public ListElement(FormattedText name, int indent, ListElement parent, String type) {
this.indent = indent;
this.parent = parent;
}
- public void addContent(ListElement content) {
+ public void addContent(Content content) {
+ applyTextAccumulator();
this.content.add(content);
}
+ private void applyTextAccumulator(){
+ if (normalTextAccumulator.length() == 0)
+ return;
+
+ content.add(new TextBlock(normalTextAccumulator.toString()));
+
+ normalTextAccumulator.setLength(0);
+ }
+
public void parse(TokenizerMatch tm) {
if (tm.isGroup(TG_DOCUMENT_PROPERTY)) {
-// System.out.println("DOCUMENT PROPERT!!!: " + tm.token);
+ // TODO
+ // System.out.println("DOCUMENT PROPERTY!!!: " + tm.token);
+ return;
+ }
+
+ if (tm.isGroup(TG_DRAWER_PROPERTY)) {
+ // TODO
+ // System.out.println("DOCUMENT PROPERTY!!!: " + tm.token);
return;
}
if (tm.isGroup(TG_NORMAL_TEXT)) {
-// System.out.println(" Plain text content: " + tm.token);
+ normalTextAccumulator.append(tm.token);
+ return;
+ }
+
+ if (tm.isGroup(TG_MULTILINE_CODE)){
+ // System.out.println(tm.toString());
+ String[] groups = tm.getRegExpGroups();
+ addContent(new MultilineCode(
+ groups[3], // language
+ groups[5] // code
+ ));
+ return;
+ }
+
+ if (tm.isGroup(TG_VERSE)){
+ String[] groups = tm.getRegExpGroups();
+ addContent(new Verse(groups[5]));
return;
}
public void toMD(StringBuilder sb, int indent) {
+ applyTextAccumulator();
+
if (this.indent >= 0) {
String2 s = new String2();
s.append(" ", indent).append(type).append(" ").append(name.toMD(indent + 2)).append("\n");
--- /dev/null
+package eu.svjatoslav.sixth.core.document.content;
+
+import eu.svjatoslav.commons.string.String2;
+
+public class MultilineCode implements Content {
+ public final String language;
+ public final String code;
+
+ public MultilineCode(String language, String code) {
+ this.language = language;
+ this.code = code;
+ }
+
+ @Override
+ public void toMD(StringBuilder sb, int indent) {
+ String2 s = new String2();
+ s.append(" ", indent).append("```" + getMDlanguage() + "\n");
+ // TODO: ensure that required indent is present
+ s.append(code + "\n");
+ s.append(" ", indent).append("```\n");
+ sb.append(s.toString());
+ }
+
+ public String getMDlanguage(){
+ // TODO: do not append ORG parameters to language, like: file: ....
+ if (language == null) return "";
+ return language;
+ }
+}
import eu.svjatoslav.sixth.core.document.text.FormattedText;
public class TextBlock implements Content {
- private FormattedText text;
+ private final FormattedText text;
+
+ public TextBlock (String contentInOrgMarkup){
+ text = FormattedText.fromOrg(contentInOrgMarkup);
+ }
@Override
public void toMD(StringBuilder sb, int indent) {
- // TODO
- // sb.append(text.toMD());
+ sb.append(text.toMD(indent) + "\n");
}
}
--- /dev/null
+package eu.svjatoslav.sixth.core.document.content;
+
+import eu.svjatoslav.commons.string.String2;
+
+public class Verse implements Content {
+ public final String verse;
+
+ public Verse(String verse) {
+ this.verse = verse;
+ }
+
+ @Override
+ public void toMD(StringBuilder sb, int indent) {
+ String2 s = new String2();
+ s.append(" ", indent).append("```\n");
+ // TODO: ensure that required indent is present
+ s.append(verse + "\n");
+ s.append(" ", indent).append("```\n");
+ sb.append(s.toString());
+ }
+}
package eu.svjatoslav.sixth.core.document.text;
-import eu.svjatoslav.commons.string.String2;
import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
import java.util.List;
import static eu.svjatoslav.sixth.core.document.Helper.TG_HYPERLINK;
+import static eu.svjatoslav.sixth.core.document.Utils.addIndentExceptFirstLine;
public class FormattedText {
List<FormattedTextElement> elements = new ArrayList<>();
for (FormattedTextElement element : elements)
sb.append(element.toMD());
- return ensureIndent(sb.toString(), indent);
- }
-
- public static String ensureIndent(String input, int indent) {
- String[] lines = input.split("\\r?\\n");
-
- StringBuilder sb = new StringBuilder();
-
- sb.append(lines[0]);
-
- for (int i = 1; i< lines.length; i++) {
- sb.append("\n");
- sb.append(new String2(" ").repeat(indent).toString());
- sb.append(lines[i]);
- }
-
- return sb.toString();
+ return addIndentExceptFirstLine(sb.toString(), indent);
}
private Tokenizer getTokenizer(String contents) {
@Override
public String toMD() {
- return "<URL: " + URL + ", LABEL: " + label + ">";
+
+ if (URL.startsWith("id:"))
+ return label; // TODO
+
+ return "[" + label + "]("+ URL + ")";
}
public static Hyperlink fromOrg(TokenizerMatch tokenizerMatch) {