Skip to content

Instantly share code, notes, and snippets.

@dmsnell
Last active April 12, 2020 03:49
Show Gist options
  • Save dmsnell/88aed8d18a9b49e5e2be262250b39be8 to your computer and use it in GitHub Desktop.
Save dmsnell/88aed8d18a9b49e5e2be262250b39be8 to your computer and use it in GitHub Desktop.
Native Java BlockParser for Gutenberg Posts
import java.util.ArrayList;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
class BlockParser {
class Block {
public String blockName;
public JsonObject attrs;
public ArrayList<Block> innerBlocks;
public String innerHTML;
public ArrayList<String> innerContent;
public Block(String blockName, JsonObject attrs, ArrayList<Block> innerBlocks, String innerHTML, ArrayList<String> innerContent) {
this.blockName = blockName;
this.attrs = attrs;
this.innerBlocks = innerBlocks;
this.innerHTML = innerHTML;
this.innerContent = innerContent;
}
@Override
public String toString() {
return String.format(
"Block(%s):\n\t%s\n\t[%s]\n\t%s\n\t%s]",
blockName,
attrs,
innerBlocks,
innerHTML,
innerContent
);
}
}
class Frame {
public Block block;
public int offset;
public int length;
public int prevOffset;
public int leadingHtmlStart;
public Frame(Block block, int offset, int length, int prevOffset, int leadingHtmlStart) {
this.block = block;
this.offset = offset;
this.length = length;
this.prevOffset = prevOffset >= 0 ? prevOffset : offset + length;
this.leadingHtmlStart = leadingHtmlStart;
}
}
class Token {
public String type;
public String name;
public JsonObject attrs;
public int offset;
public int length;
public Token(String type, String name, JsonObject attrs, int offset, int length) {
this.type = type;
this.name = name;
this.attrs = attrs;
this.offset = offset;
this.length = length;
}
}
private String document;
private int offset;
private ArrayList<Block> output;
private Stack<Frame> stack;
private Matcher tokenMatcher;
public ArrayList<Block> parse(String document) {
this.document = document;
offset = 0;
output = new ArrayList<Block>();
stack = new Stack<Frame>();
Pattern tokenPattern = Pattern.compile("<!--\\s+(?<closer>/)?wp:(?<namespace>[a-z][a-z0-9_-]*/)?(?<name>[a-z][a-z0-9_-]*)\\s+(?<attrs>\\{(?:(?:[^\\}]+|\\}+(?=\\})|(?!\\}\\s+/?-->).)*+)?\\}\\s+)?(?<void>/)?-->");
tokenMatcher = tokenPattern.matcher(document);
do {
// twiddle our thumbs
} while (proceed());
return output;
}
private boolean proceed() {
Token token = nextToken();
int stackDepth = stack.size();
// we may have some HTML soup before the next block
int leadingHtmlStart = ( token.offset > offset ) ? offset : -1;
switch (token.type) {
case "no-more-tokens":
if (0 == stackDepth) {
addFreeform();
return false;
}
if (1 == stackDepth) {
addBlockFromStack();
return false;
}
while (0 < stack.size()) {
addBlockFromStack();
}
return false;
case "void-block":
if (0 == stackDepth) {
if (leadingHtmlStart >= 0) {
output.add(Freeform(document.substring(leadingHtmlStart, token.offset)));
}
output.add(new Block(token.name, token.attrs, new ArrayList<Block>(), "", new ArrayList<String>()));
offset = token.offset + token.length;
return true;
}
addInnerBlock(
new Block(token.name, token.attrs, new ArrayList<Block>(), "", new ArrayList<String>()),
token.offset,
token.length
);
offset = token.offset + token.length;
return true;
case "block-opener":
stack.add(new Frame(
new Block(token.name, token.attrs, new ArrayList<Block>(), "", new ArrayList<String>()),
token.offset,
token.length,
token.offset + token.length,
leadingHtmlStart
));
offset = token.offset + token.length;
return true;
case "block-closer":
if (0 == stackDepth) {
addFreeform();
return false;
}
if (1 == stackDepth) {
addBlockFromStack(token.offset);
offset = token.offset + token.length;
return true;
}
Frame top = stack.pop();
String html = document.substring(top.prevOffset, token.offset);
top.block.innerHTML = top.block.innerHTML.concat(html);
top.block.innerContent.add(html);
top.prevOffset = token.offset + token.length;
addInnerBlock(top.block, top.offset, top.length, token.offset + token.length);
offset = token.offset + token.length;
return true;
default:
addFreeform();
return false;
}
}
private Token nextToken() {
if (!tokenMatcher.find()) {
return new Token("no-more-tokens", null, null, -1, -1);
}
int startedAt = tokenMatcher.start();
int length = tokenMatcher.end() - startedAt;
boolean isCloser = null != tokenMatcher.group("closer");
boolean isVoid = null != tokenMatcher.group("void");
String namespace = null != tokenMatcher.group("namespace")
? tokenMatcher.group("namespace")
: "core/";
String name = namespace.concat(tokenMatcher.group("name"));
JsonElement attrs = null;
try {
attrs = JsonParser.parseString(tokenMatcher.group("attrs"));
if (!attrs.isJsonObject()) {
attrs = new JsonObject();
}
} catch (Exception e) {
attrs = new JsonObject();
}
if (isCloser && (isVoid || null != attrs)) {
// ignore them since they don't hurt anything
}
if (isVoid) {
return new Token("void-block", name, attrs.getAsJsonObject(), startedAt, length);
}
if (isCloser) {
return new Token("block-closer", name, null, startedAt, length);
}
return new Token("block-opener", name, attrs.getAsJsonObject(), startedAt, length);
}
private Block Freeform(String innerHTML) {
ArrayList<Block> innerBlocks = new ArrayList<Block>();
ArrayList<String> innerContents = new ArrayList<String>();
innerContents.add(innerHTML);
return new Block("", null, innerBlocks, innerHTML, innerContents);
}
private void addFreeform() {
output.add(Freeform(document.substring(offset)));
}
private void addInnerBlock(Block block, int tokenStart, int tokenLength, int lastOffset) {
Frame parent = stack.peek();
parent.block.innerBlocks.add(block);
String html = document.substring(parent.prevOffset, tokenStart);
if (!html.isEmpty()) {
parent.block.innerHTML = parent.block.innerHTML.concat(html);
parent.block.innerContent.add(html);
}
parent.block.innerContent.add(null);
parent.prevOffset = lastOffset >= 0 ? lastOffset : tokenStart + tokenLength;
}
private void addInnerBlock(Block block, int tokenStart, int tokenLength) {
addInnerBlock(block, tokenStart, tokenLength, -1);
}
private void addBlockFromStack(int endOffset) {
Frame top = stack.pop();
int prevOffset = top.prevOffset;
String html = endOffset >= 0
? document.substring(prevOffset, endOffset)
: document.substring(prevOffset);
if (!html.isEmpty()) {
top.block.innerHTML = top.block.innerHTML.concat(html);
top.block.innerContent.add(html);
}
if (top.leadingHtmlStart >= 0) {
output.add(Freeform(document.substring(top.leadingHtmlStart, top.offset)));
}
output.add(top.block);
}
private void addBlockFromStack() {
addBlockFromStack(-1);
}
}
import java.util.ArrayList;
import org.jsoup.Jsoup;
public class CoverBlock {
public static void main(String args[]) {
System.out.println("Running");
BlockParser parser = new BlockParser();
var blocks = parser.parse(
"<!-- wp:paragraph -->\n" +
"<p>Answer in a comment and then click below to find out what mine is!</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:cover {\"url\":\"https://notificationsp2.files.wordpress.com/2020/03/image.png\",\"id\":332} -->\n" +
"<div class=\"wp-block-cover has-background-dim\" style=\"background-image:url(https://notificationsp2.files.wordpress.com/2020/03/image.png)\"><div class=\"wp-block-cover__inner-container\"><!-- wp:paragraph {\"align\":\"center\",\"placeholder\":\"Write title…\",\"fontSize\":\"large\"} -->\n" +
"<p class=\"has-text-align-center has-large-font-size\">Yay!</p>\n" +
"<!-- /wp:paragraph --></div></div>\n" +
"<!-- /wp:cover -->\n" +
"\n" +
"<!-- wp:a8c/spoiler -->\n" +
"<details class=\"wp-block-a8c-spoiler\"><summary>My <span class=\"has-inline-color has-vivid-green-cyan-color\">favorite</span> <em>food</em>?</summary><div style=\"margin-top:1em\"><!-- wp:list -->\n" +
"<ul><li>Something savory and sweet!</li><li>It's a candied pumpkin!</li></ul>\n" +
"<!-- /wp:list -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"center\"} -->\n" +
"<p class=\"has-text-align-center\">Arbitrary nested blocks.</p>\n" +
"<!-- /wp:paragraph --></div></details>\n" +
"<!-- /wp:a8c/spoiler -->\n" +
"\n" +
"<!-- wp:paragraph -->\n" +
"<p></p>\n" +
"<!-- /wp:paragraph -->\n"
);
System.out.println(blocks);
System.out.println(getCoverImages(blocks));
}
private static ArrayList<String> getCoverImages(ArrayList<BlockParser.Block> blocks) {
ArrayList<String> coverImages = new ArrayList<String>();
for (BlockParser.Block block: blocks) {
coverImages.addAll(getCoverImages(block));
}
return coverImages;
}
private static ArrayList<String> getCoverImages(BlockParser.Block block) {
return getCoverImages(block, new ArrayList<String>());
}
private static ArrayList<String> getCoverImages(BlockParser.Block block, ArrayList<String> results) {
if (block.blockName.equals("core/cover")) {
results.add(block.attrs.get("url").getAsString());
String style = Jsoup.parse(block.innerHTML).select("div").attr("style");
System.out.println(style);
}
if (!block.innerBlocks.isEmpty()) {
for (BlockParser.Block innerBlock: block.innerBlocks) {
getCoverImages(innerBlock, results);
}
}
return results;
}
}
javac -classpath gson-2.8.6.jar:jsoup-1.13.1.jar BlockParser.java Test.java CoverBlocks.java
// print block names
java -classpath gson-2.8.6.jar:. Test
// find cover block images
java -classpath gson-2.8.6.jar:jsoup-1.13.1.jar:. CoverBlocks
class Test {
public static void main(String args[]) {
System.out.println("Running");
BlockParser parser = new BlockParser();
var blocks = parser.parse("<!-- wp:paragraph {\"customTextColor\":\"#6c7781\",\"customFontSize\":17} -->\n" +
"<p style=\"color:#6c7781;font-size:17px\" class=\"has-text-color\"><em>It’s a whole new way to use WordPress. Try it right here!</em></p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">We call the new editor Gutenberg. The entire editing experience has been rebuilt for media rich pages and posts. Experience the flexibility that blocks will bring, whether you are building your first site, or write code for a living.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:gallery {\"ids\":[],\"columns\":4,\"align\":\"wide\",\"className\":\"gutenberg-landing\u002d\u002dfeatures-grid\"} -->\n" +
"<figure class=\"wp-block-gallery alignwide columns-4 is-cropped gutenberg-landing--features-grid\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Plugin-1-1.gif\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Do more with fewer plugins.</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Layout-3.gif\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Create modern, multimedia-heavy layouts.</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Devices-1-1.gif\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Work across all screen sizes and devices.</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Visual-1.gif\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Trust that your editor looks like your website.</figcaption></figure></li></ul></figure>\n" +
"<!-- /wp:gallery -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:wporg/download-button -->\n" +
"<div class=\"wp-block-wporg-download-button wp-block-button aligncenter\"><a class=\"wp-block-button__link has-background has-strong-blue-background-color\" href=\"https://wordpress.org/download/\" style=\"background-color:rgb(0,115,170);color:#fff\">Try it Today in WordPress</a></div>\n" +
"<!-- /wp:wporg/download-button -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"center\",\"fontSize\":\"small\",\"className\":\"gutenberg-landing\u002d\u002dbutton-disclaimer\"} -->\n" +
"<p class=\"has-text-align-center has-small-font-size gutenberg-landing--button-disclaimer\"><em>Gutenberg is available as part of WordPress 5.0 and later. The <a href=\"https://wordpress.org/plugins/classic-editor/\">Classic Editor</a> plugin allows users to switch back to the previous editor if needed. Future development will continue in the <a href=\"https://wordpress.org/plugins/gutenberg/\">Gutenberg</a> plugin.</em></p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:heading {\"align\":\"left\"} -->\n" +
"<h2 class=\"has-text-align-left\">Meet your new best friends, Blocks</h2>\n" +
"<!-- /wp:heading -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">Blocks are a great new tool for building engaging content. With blocks, you can insert, rearrange, and style multimedia content with very little technical knowledge. Instead of using custom code, you can add a block and focus on your content.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:image {\"id\":358} -->\n" +
"<figure class=\"wp-block-image\"><img src=\"https://wordpress.org/gutenberg/files/2018/07/Insert-Block-2-1.gif\" alt=\"\" class=\"wp-image-358\"/></figure>\n" +
"<!-- /wp:image -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">Without being an expert developer, you can build your own custom posts and pages. Here’s a selection of the default blocks included with Gutenberg:</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:gallery {\"ids\":[],\"columns\":8,\"align\":\"full\"} -->\n" +
"<figure class=\"wp-block-gallery alignfull columns-8 is-cropped\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Paragraph</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Heading.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Heading</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Subheading.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Subheading</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Quote.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Quote</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Image.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Image</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Gallery.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Gallery</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Cover-Image.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Cover Image</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Video.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Video</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Audio.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Audio</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Column.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Columns</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-File.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">File</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Code.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Code</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-List.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">List</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Button.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Button</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-Embeds.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">Embeds</figcaption></figure></li><li class=\"blocks-gallery-item\"><figure><img src=\"https://wordpress.org/gutenberg/files/2018/07/Block-Icon-More.png\" alt=\"\"/><figcaption class=\"blocks-gallery-item__caption\">More</figcaption></figure></li></ul></figure>\n" +
"<!-- /wp:gallery -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:heading {\"align\":\"left\"} -->\n" +
"<h2 class=\"has-text-align-left\">Be your own builder</h2>\n" +
"<!-- /wp:heading -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">A single block is nice—reliable, clear, distinct. Discover the flexibility to use media and content, side by side, driven by your vision.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:image {\"id\":98363} -->\n" +
"<figure class=\"wp-block-image\"><img src=\"https://wordpress.org/gutenberg/files/2018/08/Builder.gif\" alt=\"\" class=\"wp-image-98363\"/></figure>\n" +
"<!-- /wp:image -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:heading {\"align\":\"left\"} -->\n" +
"<h2 class=\"has-text-align-left\">Gutenberg ❤️ Developers</h2>\n" +
"<!-- /wp:heading -->\n" +
"\n" +
"<!-- wp:columns {\"className\":\"has-2-columns gutenberg-landing\u002d\u002ddevelopers-columns\"} -->\n" +
"<div class=\"wp-block-columns has-2-columns gutenberg-landing--developers-columns\"><!-- wp:column -->\n" +
"<div class=\"wp-block-column\"><!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\"><strong>Built with modern technology.</strong></p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">Gutenberg was developed on GitHub using the WordPress REST API, JavaScript, and React.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\",\"fontSize\":\"small\"} -->\n" +
"<p class=\"has-text-align-left has-small-font-size\"><a href=\"https://developer.wordpress.org/block-editor/key-concepts/\">Learn more</a></p>\n" +
"<!-- /wp:paragraph --></div>\n" +
"<!-- /wp:column -->\n" +
"\n" +
"<!-- wp:column -->\n" +
"<div class=\"wp-block-column\"><!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\"><strong>Designed for compatibility.</strong></p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">We recommend migrating features to blocks, but support for existing WordPress functionality remains. There will be transition paths for shortcodes, meta-boxes, and Custom Post Types.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\",\"fontSize\":\"small\"} -->\n" +
"<p class=\"has-text-align-left has-small-font-size\"><a href=\"https://developer.wordpress.org/block-editor/contributors/faq/\">Learn more</a></p>\n" +
"<!-- /wp:paragraph --></div>\n" +
"<!-- /wp:column --></div>\n" +
"<!-- /wp:columns -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:heading {\"align\":\"left\"} -->\n" +
"<h2 class=\"has-text-align-left\">The editor is just the beginning</h2>\n" +
"<!-- /wp:heading -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"left\"} -->\n" +
"<p class=\"has-text-align-left\">Gutenberg is more than an editor. It’s also the foundation that’ll revolutionize customization and site building in WordPress.</p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:quote {\"align\":\"left\",\"className\":\"is-style-large\"} -->\n" +
"<blockquote class=\"wp-block-quote has-text-align-left is-style-large\"><p>\"This will make running your own blog a viable alternative again.\"</p><cite>— <a href=\"https://twitter.com/azumbrunnen_/status/1019347243084800005\">Adrian Zumbrunnen</a></cite></blockquote>\n" +
"<!-- /wp:quote -->\n" +
"\n" +
"<!-- wp:quote {\"align\":\"left\",\"className\":\"is-style-large\"} -->\n" +
"<blockquote class=\"wp-block-quote has-text-align-left is-style-large\"><p>\"The web up until this point has been confined to some sort of rectangular screen. But that is not how it’s going to be. Gutenberg has the potential of moving us into the next time.\"</p><cite>— <a href=\"https://wordpress.tv/2017/12/10/morten-rand-hendriksen-gutenberg-and-the-wordpress-of-tomorrow/\">Morten Rand-Hendriksen</a></cite></blockquote>\n" +
"<!-- /wp:quote -->\n" +
"\n" +
"<!-- wp:quote {\"align\":\"left\",\"className\":\"is-style-large\"} -->\n" +
"<blockquote class=\"wp-block-quote has-text-align-left is-style-large\"><p>\"The Gutenberg editor has some great assets that could genuinely help people to write better texts.\"</p><cite>— <a href=\"https://yoast.com/writing-with-gutenberg/\">Marieke van de Rakt</a></cite></blockquote>\n" +
"<!-- /wp:quote -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:wporg/download-button -->\n" +
"<div class=\"wp-block-wporg-download-button wp-block-button aligncenter\"><a class=\"wp-block-button__link has-background has-strong-blue-background-color\" href=\"https://wordpress.org/download/\" style=\"background-color:rgb(0,115,170);color:#fff\">Try it Today in WordPress</a></div>\n" +
"<!-- /wp:wporg/download-button -->\n" +
"\n" +
"<!-- wp:paragraph {\"align\":\"center\",\"fontSize\":\"small\",\"className\":\"gutenberg-landing\u002d\u002dbutton-disclaimer\"} -->\n" +
"<p class=\"has-text-align-center has-small-font-size gutenberg-landing--button-disclaimer\"><em>Gutenberg is available as part of WordPress 5.0 and later. The <a href=\"https://wordpress.org/plugins/classic-editor/\">Classic Editor</a> plugin allows users to switch back to the previous editor if needed. Future development will continue in the <a href=\"https://wordpress.org/plugins/gutenberg/\">Gutenberg</a> plugin.</em></p>\n" +
"<!-- /wp:paragraph -->\n" +
"\n" +
"<!-- wp:spacer -->\n" +
"<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" +
"<!-- /wp:spacer -->\n" +
"\n" +
"<!-- wp:heading {\"align\":\"left\"} -->\n" +
"<h2 class=\"has-text-align-left\">Dig in deeper</h2>\n" +
"<!-- /wp:heading -->\n" +
"\n" +
"<!-- wp:list -->\n" +
"<ul><li><a href=\"https://make.wordpress.org/core/2017/01/17/editor-technical-overview\">Gutenberg Editor Technical Overview</a></li><li><a href=\"https://developer.wordpress.org/block-editor/contributors/design/\">Gutenberg Design Principles</a></li><li><a href=\"https://make.wordpress.org/core/tag/gutenberg/\">Development updates on make.wordpress.org</a></li><li><a href=\"https://wordpress.tv/?s=gutenberg\">WordPress.tv Talks about Gutenberg</a></li><li><a href=\"https://developer.wordpress.org/block-editor/contributors/faq/\">FAQs</a></li></ul>\n" +
"<!-- /wp:list -->\n"
);
System.out.println(blocks);
for (BlockParser.Block block: blocks) {
printNames(block);
}
}
private static void printNames(BlockParser.Block block) {
printNames(block, "");
}
private static void printNames(BlockParser.Block block, String indent) {
System.out.format("%s%s\n", indent, block.blockName);
if (!block.innerBlocks.isEmpty()) {
for (BlockParser.Block innerBlock: block.innerBlocks) {
printNames(innerBlock, indent.concat("\t"));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment