Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import com.g2forge.alexandria.java.close.ICloseable;
import com.g2forge.enigma.backend.convert.textual.ITextualRenderContext;
import com.g2forge.enigma.document.convert.md.linebreak.ILineBreakStrategy;

public interface IMDRenderContext extends ITextualRenderContext<Object, IMDRenderContext> {
public LineBreakStrategy getLineBreakStrategy();
public ILineBreakStrategy getLineBreakStrategy();

public int getSectionLevel();

public ICloseable openLineBreakStrategy(LineBreakStrategy strategy);
public int getIndentLevel();

public ICloseable openLineBreakStrategy(ILineBreakStrategy strategy);

public ICloseable openSection();

public ICloseable openIndent();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

import com.g2forge.alexandria.java.close.ICloseable;
import com.g2forge.alexandria.java.core.enums.EnumException;
import com.g2forge.alexandria.java.function.IConsumer1;
import com.g2forge.alexandria.java.function.IConsumer2;
import com.g2forge.alexandria.java.function.IFunction1;
import com.g2forge.alexandria.java.function.ISupplier;
import com.g2forge.alexandria.java.type.function.TypeSwitch1;
import com.g2forge.enigma.backend.ITextAppender;
import com.g2forge.enigma.backend.convert.ARenderer;
import com.g2forge.enigma.backend.convert.IExplicitRenderable;
import com.g2forge.enigma.backend.convert.IRendering;
import com.g2forge.enigma.backend.convert.textual.ATextualRenderer;
import com.g2forge.enigma.backend.text.model.modifier.TextNestedModified;
import com.g2forge.enigma.document.convert.md.linebreak.ILineBreakStrategy;
import com.g2forge.enigma.document.convert.md.linebreak.LineBreakStrategy;
import com.g2forge.enigma.document.model.Block;
import com.g2forge.enigma.document.model.Definition;
import com.g2forge.enigma.document.model.DocList;
Expand All @@ -34,52 +38,58 @@
@RequiredArgsConstructor
public class MDRenderer extends ATextualRenderer<Object, IMDRenderContext> {
protected class MDRenderContext extends ARenderContext implements IMDRenderContext {
protected final Stack<LineBreakStrategy> lineBreakStrategies = new Stack<>();

protected final Stack<ICloseable> stack = new Stack<>();

public MDRenderContext(TextNestedModified.TextNestedModifiedBuilder builder) {
super(builder);
}
@Getter
protected ILineBreakStrategy lineBreakStrategy = LineBreakStrategy.None;

@Override
public LineBreakStrategy getLineBreakStrategy() {
if (lineBreakStrategies.isEmpty()) return LineBreakStrategy.None;
return lineBreakStrategies.peek();
}
@Getter
protected int sectionLevel = 1;

@Override
public int getSectionLevel() {
return stack.size() + 1;
@Getter
protected int indentLevel;

public MDRenderContext(TextNestedModified.TextNestedModifiedBuilder builder) {
super(builder);
}

@Override
protected IMDRenderContext getThis() {
return this;
}

@Override
public ICloseable openLineBreakStrategy(LineBreakStrategy strategy) {
lineBreakStrategies.push(strategy);
final int size = lineBreakStrategies.size();
return () -> {
if (lineBreakStrategies.size() != size) throw new IllegalStateException();
lineBreakStrategies.pop();
};
}

@Override
public ICloseable openSection() {
protected <T> ICloseable open(ISupplier<T> start, IConsumer1<T> stop) {
final T value = start.get();
final ICloseable retVal = new ICloseable() {
@Override
public void close() {
if (stack.peek() != this) throw new IllegalArgumentException();
stack.pop();
stop.accept(value);
}
};
stack.push(retVal);
return retVal;
}

@Override
public ICloseable openLineBreakStrategy(ILineBreakStrategy strategy) {
return open(() -> {
final ILineBreakStrategy retVal = lineBreakStrategy;
lineBreakStrategy = strategy;
return retVal;
}, prev -> lineBreakStrategy = prev);
}

@Override
public ICloseable openSection() {
return open(() -> sectionLevel++, prev -> sectionLevel = prev);
}

@Override
public ICloseable openIndent() {
return open(() -> indentLevel++, prev -> indentLevel = prev);
}
}

protected static class MDRendering extends ARenderer.ARendering<Object, IMDRenderContext, IExplicitRenderable<? super IMDRenderContext>> {
Expand Down Expand Up @@ -127,18 +137,19 @@ protected void extend(TypeSwitch1.FunctionBuilder<Object, IExplicitRenderable<?
});

builder.add(Block.class, md -> c -> {
try (final ICloseable strategy = c.openLineBreakStrategy(LineBreakStrategy.fromBlockType(md.getType()))) {
try (final ICloseable strategy = c.openLineBreakStrategy(LineBreakStrategy.fromBlockType(c, md.getType()))) {
boolean first = true;
for (IBlock content : md.getContents()) {
if (first) first = false;
else c.getLineBreakStrategy().beforeItem(c, first);
else c.getLineBreakStrategy().beforeItem(c, first, content);
c.render(content, IBlock.class);
}
}
});
builder.add(DocList.class, md -> c -> {
final java.util.List<IDocListItem> items = md.getItems();
for (int i = 0; i < items.size(); i++) {
for (int j = 0; j < c.getIndentLevel(); j++) c.append(" ");
switch (md.getMarker()) {
case Ordered:
c.append("* ");
Expand All @@ -147,7 +158,9 @@ protected void extend(TypeSwitch1.FunctionBuilder<Object, IExplicitRenderable<?
c.append(String.format("%1$d. ", i + 1));
break;
}
c.render(items.get(i), IBlock.class);
try (ICloseable indent = c.openIndent()) {
c.render(items.get(i), IBlock.class);
}
if (i < (items.size() - 1)) c.newline();
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.g2forge.enigma.document.convert.md.linebreak;

import com.g2forge.enigma.document.convert.md.IMDRenderContext;
import com.g2forge.enigma.document.model.IDocListItem;

public interface ILineBreakStrategy {
public void beforeItem(IMDRenderContext context, boolean first, IDocListItem item);

public void text(IMDRenderContext context, String text);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.g2forge.enigma.document.convert.md;
package com.g2forge.enigma.document.convert.md.linebreak;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.g2forge.alexandria.java.core.enums.EnumException;
import com.g2forge.enigma.document.convert.md.IMDRenderContext;
import com.g2forge.enigma.document.model.Block;
import com.g2forge.enigma.document.model.IDocListItem;

public enum LineBreakStrategy {
public enum LineBreakStrategy implements ILineBreakStrategy {
None,
Item {
@Override
public void beforeItem(IMDRenderContext context, boolean first) {
public void beforeItem(IMDRenderContext context, boolean first, IDocListItem item) {
if (!first) context.newline().newline();
}
},
Expand All @@ -28,20 +30,25 @@ public void text(IMDRenderContext context, String text) {
}
};

public static LineBreakStrategy fromBlockType(Block.Type type) {
public static ILineBreakStrategy fromBlockType(IMDRenderContext context, Block.Type type) {
final LineBreakStrategy base;
switch (type) {
case Document:
case Block:
return Item;
base = Item;
break;
case Paragraph:
case ListItem:
return Period;
base = Period;
break;
default:
throw new EnumException(Block.Type.class, type);
}
if (context.getIndentLevel() > 0) return new ListLineBreakStrategy(base);
return base;
}

public void beforeItem(IMDRenderContext context, boolean first) {}
public void beforeItem(IMDRenderContext context, boolean first, IDocListItem item) {}

public void text(IMDRenderContext context, String text) {
context.append(text);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.g2forge.enigma.document.convert.md.linebreak;

import com.g2forge.enigma.document.convert.md.IMDRenderContext;
import com.g2forge.enigma.document.model.DocList;
import com.g2forge.enigma.document.model.IDocListItem;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class ListLineBreakStrategy implements ILineBreakStrategy {
protected final LineBreakStrategy base;

@Override
public void beforeItem(IMDRenderContext context, boolean first, IDocListItem item) {
if (!first) {
if (!(item instanceof DocList)) context.append("\\");
context.newline();
}
}

@Override
public void text(IMDRenderContext context, String text) {
context.append(text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,48 @@
import com.g2forge.enigma.document.model.Block;
import com.g2forge.enigma.document.model.Definition;
import com.g2forge.enigma.document.model.DocList;
import com.g2forge.enigma.document.model.DocList.DocListBuilder;
import com.g2forge.enigma.document.model.Emphasis;
import com.g2forge.enigma.document.model.Text;
import com.g2forge.enigma.document.model.Block.BlockBuilder;

public class TestMD {
protected final MDRenderer renderer = new MDRenderer();

protected void assertEquals(final String filename, final Object actual) {
final String expected = HResource.read(getClass(), filename, true);
Assert.assertEquals(expected, renderer.render(actual));
}

@Test
public void help() {
final DocList.DocListBuilder builder = DocList.builder().marker(DocList.Marker.Ordered);
builder.item(Definition.builder().term(new Emphasis(Emphasis.Type.Code, new Text("-x"))).body(new Text("This is a sentence about the 'x' option!")).build());
builder.item(Definition.builder().term(new Emphasis(Emphasis.Type.Code, new Text("-y"))).body(new Text("Some description of another option")).build());
final DocList actual = builder.build();

final String expected = HResource.read(getClass(), "help.md", true);
Assert.assertEquals(expected, renderer.render(actual));
assertEquals("help.md", builder.build());
}

@Test
public void lists() {
final BlockBuilder builder = Block.builder().type(Block.Type.Block);
final DocListBuilder list0 = DocList.builder().marker(DocList.Marker.Ordered);
list0.item(Block.builder().type(Block.Type.Block).content(new Text("Item 1 Line 1")).content(new Text("Item 1 Line 2")).build());

final DocListBuilder list1 = DocList.builder().marker(DocList.Marker.Ordered);
list1.item(new Text("Item 2A"));
list1.item(new Text("Item 2B"));
list0.item(Block.builder().type(Block.Type.Block).content(new Text("Item 2")).content(list1.build()).build());

builder.content(list0.build());
assertEquals("lists.md", builder.build());
}

@Test
public void simple() {
final BlockBuilder builder = Block.builder().type(Block.Type.Block);
builder.content(DocList.builder().marker(DocList.Marker.Ordered).item(new Text("Item 1")).build());
builder.content(new Text("A paragraph goes here."));
builder.content(DocList.builder().marker(DocList.Marker.Numbered).item(new Text("Item 2")).item(new Text("Item 3")).build());
final Block actual = builder.build();

final String expected = HResource.read(getClass(), "simple.md", true);
Assert.assertEquals(expected, renderer.render(actual));
assertEquals("simple.md", builder.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* Item 1 Line 1\
Item 1 Line 2
* Item 2
* Item 2A
* Item 2B