Fixed last content element and list title continuation detections.
Properly handle list depth decrease.
Better parsing of multiline list title.
package eu.svjatoslav.sixth.core.document;
import eu.svjatoslav.commons.string.String2;
-import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
import eu.svjatoslav.sixth.core.document.content.ListElement;
import eu.svjatoslav.sixth.core.document.text.FormattedText;
public void parse(TokenizerMatch tm){
+ int indent = Utils.getLineIndent(tm.token);
+ if (indent > -1 && indent <= currentListElement.indent) handleListDepthDecrease(indent);
+
if (tm.isGroup(TG_LIST)){
parseList(tm);
return;
currentListElement.parse(tm);
}
+ private void handleListDepthDecrease(int indent) {
+ while (true){
+ if (currentListElement.parent.indent <= indent){
+ currentListElement = currentListElement.parent;
+ return;
+ }
+ currentListElement = currentListElement.parent;
+ }
+ }
+
private void parseList(TokenizerMatch tm) {
String[] listSections = tm.getRegExpGroups();
int indent = listSections[0].length();
String type = listSections[1];
- FormattedText title = FormattedText.fromOrg(parseFullListTitle(getPartialTitle(listSections), tm.getTokenizer(), indent));
+
+ String title = getPartialTitle(listSections);
if (indent > currentListElement.indent){
// list dept increases
return;
}
- // list dept decreases
- while (true){
- if (currentListElement.parent.indent <= indent){
- ListElement newElement = new ListElement(title, indent, currentListElement.parent.parent, type);
- currentListElement.parent.parent.addContent(newElement);
- currentListElement = newElement;
- return;
- }
- currentListElement = currentListElement.parent;
- }
-
+ throw new RuntimeException("Impossible condition reached. Must be a bug!");
}
private String getPartialTitle(String[] listSections) {
return listSections.length > 2 ? listSections[2] : "";
}
- private String parseFullListTitle(String partialTitle, Tokenizer tokenizer, int listIndent){
- StringBuilder sb = new StringBuilder();
- sb.append(partialTitle);
-
- while (tokenizer.hasMoreContent()){
- final TokenizerMatch tm = tokenizer.getNextToken();
-
- if (isContentContinuation(tm, listIndent, Helper.TG_NORMAL_TEXT)){
- String titleContinuation = tm.token.substring(listIndent).trim();
- sb.append("\n").append(titleContinuation);
- continue;
- }
-
- tokenizer.unreadToken();
- break;
- }
-
- return sb.toString();
- }
-
- public static boolean isContentContinuation(TokenizerMatch tm, int requiredIndent, String requiredGroup) {
- if (tm.token.length() <= requiredIndent) return false;
-
- return tm.isGroup(requiredGroup) && tm.token.substring(0, requiredIndent +1).trim().length() == 0;
- }
-
}
public class Utils {
+ public static final char[] whitespace = new char[]{'\n', '\r', ' ', '\t'};
+
public static String addIndentExceptFirstLine(String input, int indent) {
String[] lines = input.split("\\r?\\n");
}
public static boolean isBlank(String s){
- return s.trim().length() == 0;
+ for (char c : s.toCharArray())
+ if (!isWhitespaceChar(c)) return false;
+
+ return true;
+ }
+
+ /**
+ * @return line indent in characters or -1 if line is blank or empty
+ */
+ public static int getLineIndent(String line){
+ for (int i = 0; i < line.length(); i++) {
+ if (!isWhitespaceChar(line.charAt(i)))
+ return i;
+ }
+ return -1;
+ }
+
+ public static boolean isWhitespaceChar(char c){
+ for (char whitespaceChar : whitespace)
+ if (whitespaceChar == c) return true;
+
+ return false;
+ }
+
+ public static String removePrefix(String string, int charsToRemove){
+ String2 s = new String2(string);
+ s.trimPrefix(charsToRemove);
+ return s.toString();
}
}
import static eu.svjatoslav.sixth.core.document.Helper.*;
import static eu.svjatoslav.sixth.core.document.Utils.isBlank;
+import static eu.svjatoslav.sixth.core.document.Utils.removePrefix;
public class ListElement implements Content {
- public final FormattedText name;
+ StringBuilder nameInOrg = new StringBuilder();
public final int indent;
public final ListElement parent;
private final String type;
private final List<Content> content = new ArrayList<>();
- public ListElement(FormattedText name, int indent, ListElement parent, String type) {
+ public ListElement(String nameInOrg, int indent, ListElement parent, String type) {
this.indent = indent;
- this.name = name;
+ this.nameInOrg.append(nameInOrg);
this.type = type;
this.parent = parent;
}
Content contentElement = findCurrentContentElement();
if (contentElement == null) return false;
- return contentElement.getClass().isInstance(aClass);
+ return aClass.isInstance(contentElement);
}
public void addContent(Content contentElement){
TextBlock textBlock;
if (isLastContentElement(TextBlock.class)) {
textBlock = ((TextBlock) findCurrentContentElement());
+ } if (content.isEmpty()){
+ // list title continuation
+ nameInOrg.append("\n").append(removePrefix(tm.token, indent + 2));
+ return;
} else {
textBlock = new TextBlock();
content.add(textBlock);
}
- textBlock.addContent(tm.token + "\n");
+ textBlock.addContent(tm.token);
}
private void parseDocumentProperty(TokenizerMatch tm) {
if (this.indent >= 0) {
String2 s = new String2();
- s.append(" ", indent).append(type).append(" ").append(name.toMD(indent + 2)).append("\n");
+ s.append(" ", indent).append(type).append(" ").append(getName().toMD(indent + 2)).append("\n");
sb.append(s.toString());
}
}
}
+ private FormattedText getName(){
+ return FormattedText.fromOrg(nameInOrg.toString());
+ }
+
private void disablePlantUmlExport() {
for (int i = 0; i< (content.size()-2); i++){
if (!(content.get(i) instanceof MultilineCode)) continue;
MultilineCode code = (MultilineCode) content.get(i);
- if (!"plantuml".equalsIgnoreCase(code.language)) continue;;
+ if (!"plantuml".equalsIgnoreCase(code.language)) continue;
if (!(content.get(i+1) instanceof DocumentPropertyCollection)) continue;
DocumentPropertyCollection property = (DocumentPropertyCollection) content.get(i+1);
- if (!property.hasProperty("results")) continue;;
+ if (!property.hasProperty("results")) continue;
if (!(content.get(i+2) instanceof TextBlock)) continue;
TextBlock textBlock = (TextBlock) content.get(i+2);