Skip to content

Commit

Permalink
Fixes asciidoctor#326. Added method AbstractBlock.parse(List<String>)…
Browse files Browse the repository at this point in the history
… to append raw asciidoctor content to a block
  • Loading branch information
robertpanzer committed Aug 1, 2015
1 parent b8795c5 commit b746c8c
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 10 deletions.
Expand Up @@ -49,4 +49,5 @@ public interface AbstractBlock extends AbstractNode {
* @return the source location of this block or {@code null} if the {@code sourcemap} option is not enabled when loading the document.
*/
Cursor getSourceLocation();

}
@@ -1,13 +1,13 @@
package org.asciidoctor.ast;

import java.util.List;
import java.util.Map;

import org.asciidoctor.internal.RubyBlockListDecorator;
import org.asciidoctor.internal.RubyHashUtil;
import org.jruby.RubyArray;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.List;
import java.util.Map;

public class AbstractBlockImpl extends AbstractNodeImpl implements AbstractBlock {

private static final String BLOCK_CLASS = "Block";
Expand Down
Expand Up @@ -3,7 +3,9 @@
import org.asciidoctor.Options;
import org.asciidoctor.ast.*;
import org.asciidoctor.internal.JRubyRuntimeContext;
import org.asciidoctor.extension.ReaderImpl;
import org.asciidoctor.internal.RubyHashUtil;
import org.asciidoctor.internal.RubyObjectWrapper;
import org.asciidoctor.internal.RubyUtils;
import org.jruby.Ruby;
import org.jruby.RubyArray;
Expand Down Expand Up @@ -299,4 +301,66 @@ public Document createDocument(Document parentDocument) {
return (Document) NodeConverter.createASTNode(runtime, DOCUMENT_CLASS, runtime.getNil(), options);
}

/**
* Parses the given raw asciidoctor content, parses it and appends it as children to the given parent block.
* <p>The following example will add two paragraphs with the role {@code newcontent} to all top
* level sections of a document:
* <pre>
* <verbatim>
* Asciidoctor asciidoctor = ...
* asciidoctor.javaExtensionRegistry().treeprocessor(new Treeprocessor() {
* DocumentRuby process(DocumentRuby document) {
* for (AbstractBlock block: document.getBlocks()) {
* if (block instanceof Section) {
* parseContent(block, Arrays.asList(new String[]{
* "[newcontent]",
* "This is new content"
* "",
* "[newcontent]",
* "This is also new content"}));
* }
* }
* }
* });
* </verbatim>
* </pre>
*
* @param parent The block to which the parsed content should be added as children.
* @param lines Raw asciidoctor content
*/
public void parseContent(AbstractBlock parent, List<String> lines) {
Ruby runtime = JRubyRuntimeContext.get(parent);
Parser parser = new Parser(runtime, parent, ReaderImpl.createReader(runtime, lines));

AbstractBlock nextBlock = parser.nextBlock();
while (nextBlock != null) {
parent.append(nextBlock);
nextBlock = parser.nextBlock();
}
}

private class Parser extends RubyObjectWrapper {

private final Reader reader;
private final AbstractBlock parent;

public Parser(Ruby runtime, AbstractBlock parent, Reader reader) {
super(runtime.getModule("Asciidoctor").getClass("Parser"));

this.reader = reader;
this.parent = parent;
}

public AbstractBlock nextBlock() {
if (!reader.hasMoreLines()) {
return null;
}
IRubyObject nextBlock = getRubyProperty("next_block", reader, ((AbstractBlockImpl)parent).getRubyObject());
if (nextBlock.isNil()) {
return null;
} else {
return (AbstractBlock) NodeConverter.createASTNode(nextBlock);
}
}
}
}
@@ -1,6 +1,9 @@
package org.asciidoctor.extension;

import org.asciidoctor.internal.RubyObjectWrapper;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.List;
Expand All @@ -11,6 +14,16 @@ public ReaderImpl(IRubyObject rubyNode) {
super(rubyNode);
}

static ReaderImpl createReader(Ruby runtime, List<String> lines) {
RubyArray rubyLines = runtime.newArray(lines.size());
for (String line: lines) {
rubyLines.add(runtime.newString(line));
}

RubyClass readerClass = runtime.getModule("Asciidoctor").getClass("Reader");
return new ReaderImpl(readerClass.callMethod("new", rubyLines));
}

@Override
public int getLineno() {
return getLineNumber();
Expand Down
@@ -1,10 +1,8 @@
package org.asciidoctor.extension.processorproxies;

import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.ast.AbstractNodeImpl;
import org.asciidoctor.ast.NodeConverter;
import org.asciidoctor.extension.BlockProcessor;
import org.asciidoctor.extension.Reader;
import org.asciidoctor.extension.ReaderImpl;
import org.asciidoctor.internal.RubyHashMapDecorator;
import org.asciidoctor.internal.RubyHashUtil;
Expand Down
Expand Up @@ -110,7 +110,11 @@ protected IRubyObject getRubyProperty(String propertyName, Object... args) {
} else {
IRubyObject[] rubyArgs = new IRubyObject[args.length];
for (int i = 0; i < args.length; i++) {
rubyArgs[i] = JavaEmbedUtils.javaToRuby(runtime, args[i]);
if (args[i] instanceof RubyObjectWrapper) {
rubyArgs[i] = ((RubyObjectWrapper)args[i]).getRubyObject();
} else {
rubyArgs[i] = JavaEmbedUtils.javaToRuby(runtime, args[i]);
}
}
result = rubyNode.callMethod(threadContext, propertyName, rubyArgs);
}
Expand Down
Expand Up @@ -2,13 +2,13 @@ package org.asciidoctor.extension

import org.asciidoctor.Asciidoctor
import org.asciidoctor.OptionsBuilder
import org.asciidoctor.ast.AbstractBlock
import org.asciidoctor.ast.Block
import org.asciidoctor.ast.DocumentRuby
import org.asciidoctor.ast.Document
import org.asciidoctor.ast.Section
import org.jboss.arquillian.spock.ArquillianSputnik
import org.jboss.arquillian.test.api.ArquillianResource
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.junit.runner.RunWith
import spock.lang.Specification

Expand Down Expand Up @@ -40,7 +40,7 @@ more text
int lastid = 0

@Override
DocumentRuby process(DocumentRuby document) {
Document process(Document document) {
document.blocks.findAll { block -> block instanceof Section }.each {
block ->
Block newBlock = createBlock(block, 'paragraph', additionalText, [:])
Expand All @@ -55,11 +55,83 @@ more text
String result = asciidoctor.convert(this.document, OptionsBuilder.options().headerFooter(false))

then:
Document htmlDocument = Jsoup.parse(result)
org.jsoup.nodes.Document htmlDocument = Jsoup.parse(result)
!htmlDocument.select('#NewBlock_0').empty
!htmlDocument.select('#NewBlock_1').empty
htmlDocument.select('#NewBlock_2').empty

}

def "should be able to append and parse raw asciidoc content via Block_parse"() {

given:
final String newContentClass = 'newcontent'
final List<String> contentToAdd = """
[.$newContentClass]
This is content added by an extension.
[.$newContentClass]
And this as well
""".readLines()

asciidoctor.javaExtensionRegistry().treeprocessor(new Treeprocessor() {
@Override
Document process(Document document) {
document.blocks.findAll { block -> block instanceof Section }.each {
section ->
parseContent(section, contentToAdd)
}
document
}
})

when:
String result = asciidoctor.convert(document, OptionsBuilder.options().headerFooter(false))

then:
org.jsoup.nodes.Document htmlDocument = Jsoup.parse(result)
htmlDocument.select('.sect1').every {
section ->
section.select('.paragraph').with {
paragraphs ->
!paragraphs[0].hasClass(newContentClass) &&
paragraphs[1].hasClass(newContentClass) &&
paragraphs[2].hasClass(newContentClass)
}
}
}

def "should invoke block macro processor on additionally parsed content"() {

given:
final List<String> contentToAdd = '''
testmacro::target[]
'''.readLines()

final String expectedContent = 'Generated by BlockMacro'

asciidoctor.javaExtensionRegistry().treeprocessor(new Treeprocessor() {
@Override
Document process(Document document) {
document.blocks.findAll { block -> block instanceof Section }.each {
block ->
parseContent(block, contentToAdd)
}
document
}
})

asciidoctor.javaExtensionRegistry().blockMacro(new BlockMacroProcessor('testmacro'){
@Override
Object process(AbstractBlock parent, String target, Map<String, Object> attributes) {
createBlock(parent, 'paragraph', expectedContent, [:])
}
})

when:
String result = asciidoctor.convert(document, OptionsBuilder.options().headerFooter(false))

then:
result.contains(expectedContent)
}
}

0 comments on commit b746c8c

Please sign in to comment.