Skip to content
Closed
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
7 changes: 6 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 1.4.0 (2013-06-23)
--------------------------
- Added basic plugin support (see #77, thx to James Roper and also Jakub Jirutka)


Version 1.3.0 (2013-06-04)
--------------------------
- Upgraded to parboiled 1.1.5
Expand Down Expand Up @@ -134,4 +139,4 @@ Version 0.8.1.0 (2010-06-08)

Version 0.8.0.1 (2010-04-30)
----------------------------
first public release
first public release
21 changes: 19 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Introduction
_pegdown_ is a pure Java library for clean and lightweight [Markdown] processing based on a [parboiled] PEG parser.

_pegdown_ is nearly 100% compatible with the original Markdown specification and fully passes the original Markdown test suite.
On top of the standard Markdown feature set _pegdown_ implements a number of extensions similar to what other popular Markdown processors offer.
On top of the standard Markdown feature set _pegdown_ implements a number of extensions similar to what other popular Markdown processors offer. You can also extend _pegdown_ by your own plugins!
Currently _pegdown_ supports the following extensions over standard Markdown:

* SMARTS: Beautifies apostrophes, ellipses ("..." and ". . .") and dashes ("--" and "---")
Expand Down Expand Up @@ -34,7 +34,7 @@ Installation
You have two options:

* Download the JAR for the latest version from [here](http://repo1.maven.org/maven2/org/pegdown/pegdown/).
_pegdown_ 1.3.0 has only one dependency: [parboiled for Java][parboiled], version 1.1.5.
_pegdown_ 1.4.0 has only one dependency: [parboiled for Java][parboiled], version 1.1.5.

* The pegdown artifact is also available from maven central with group id **org.pegdown** and artifact-id **pegdown**.

Expand Down Expand Up @@ -62,6 +62,18 @@ concurrent accesses, since neither the [PegDownProcessor] nor the underlying par
See <http://sirthias.github.com/pegdown/api> for the pegdown API documentation.


Plugins
-------

Since parsing and serialisation are two different things, there are two different plugin mechanisms, one for the parser, and one for the [ToHtmlSerializer]. Most plugins would probably implement both, but it is possible that a plugin might just implement the parser plugin interface.

For the parser, there are two plugin points, one for inline plugins (inside a paragraph), and one for block plugins. These are provided to the parser using the [PegDownPlugins] class. For convenience of use, this comes with its own builder. You can either pass individual rules to this builder (which is what you probably would do if you were using Scala rules), but you can also pass it a parboiled Java parser class which implements either [InlinePluginParser] or [BlockPluginParser] or both. [PegDownPlugins] will enhance this parser for you, so as a user of a plugin you just need to pass the class to it (and the arguments for that classes constructor, if any). To implement the plugin, you would write a normal parboiled parser, and implement the appropriate parser plugin interface. You can extend the pegdown parser, this is useful if you want to reuse any of its rules.

For the serializer, there is [ToHtmlSerializerPlugin] interface. It is called when a node that the [ToHtmlSerializer] doesn't know how to process is encountered (i.e. one produced by a parser plugin). Its `accept` method is passed the node, the visitor (so if the node contains child nodes, they can be rendered using the parent), and the printer for the plugin to print to. The `accept` method returns true if it knew how to handle the node, or false if otherwise, and the [ToHtmlSerializer] loops through each plugin, breaking when it reaches one that returns true, and if it finds none, throws an exception like it used to.

As an very simple example you might want to take a look at the [sources of the PluginParser test class][PluginParser].


Parsing Timeouts
----------------

Expand Down Expand Up @@ -116,3 +128,8 @@ Along with any patches, please state that the patch is your original work and th
[idea-markdown plugin]: https://github.com/nicoulaj/idea-markdown
[SBT]: http://www.scala-sbt.org/
[Node]: http://www.decodified.com/pegdown/api/org/pegdown/ast/Node.html
[PegDownPlugins]: http://github.com/sirthias/pegdown/blob/master/src/main/java/org/pegdown/plugins/PegDownPlugins.java
[InlinePluginParser]: http://github.com/sirthias/pegdown/blob/master/src/main/java/org/pegdown/plugins/InlinePluginParser.java
[BlockPluginParser]: http://github.com/sirthias/pegdown/blob/master/src/main/java/org/pegdown/plugins/BlockPluginParser.java
[ToHtmlSerializerPlugin]: http://github.com/sirthias/pegdown/blob/master/src/main/java/org/pegdown/plugins/ToHtmlSerializerPlugin.java
[PluginParser]: http://github.com/sirthias/pegdown/blob/master/src/test/java/org/pegdown/PluginParser.java
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name := "pegdown"

version := "1.3.0"
version := "1.4.0"

homepage := Some(new URL("http://pegdown.org"))

Expand Down Expand Up @@ -72,4 +72,4 @@ pomExtra :=
<id>sirthias</id>
<name>Mathias Doenitz</name>
</developer>
</developers>
</developers>
11 changes: 10 additions & 1 deletion src/main/java/org/pegdown/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.parboiled.support.Var;
import org.pegdown.ast.*;
import org.pegdown.ast.SimpleNode.Type;
import org.pegdown.plugins.PegDownPlugins;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -63,14 +64,20 @@ public ParseRunner<Node> get(Rule rule) {
protected final int options;
protected final long maxParsingTimeInMillis;
protected final ParseRunnerProvider parseRunnerProvider;
protected final PegDownPlugins plugins;
final List<AbbreviationNode> abbreviations = new ArrayList<AbbreviationNode>();
final List<ReferenceNode> references = new ArrayList<ReferenceNode>();
long parsingStartTimeStamp = 0L;

public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider) {
public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider, PegDownPlugins plugins) {
this.options = options;
this.maxParsingTimeInMillis = maxParsingTimeInMillis;
this.parseRunnerProvider = parseRunnerProvider;
this.plugins = plugins;
}

public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider) {
this(options, maxParsingTimeInMillis, parseRunnerProvider, PegDownPlugins.NONE);
}

public RootNode parse(char[] source) {
Expand Down Expand Up @@ -98,6 +105,7 @@ public Rule Block() {
return Sequence(
ZeroOrMore(BlankLine()),
FirstOf(new ArrayBuilder<Rule>()
.add(plugins.getBlockPluginRules())
.add(BlockQuote(), Verbatim())
.addNonNulls(ext(ABBREVIATIONS) ? Abbreviation() : null)
.add(Reference(), HorizontalRule(), Heading(), OrderedList(), BulletList(), HtmlBlock())
Expand Down Expand Up @@ -569,6 +577,7 @@ public Rule NonAutoLinkInline() {

public Rule NonLinkInline() {
return FirstOf(new ArrayBuilder<Rule>()
.add(plugins.getInlinePluginRules())
.add(Str(), Endline(), UlOrStarLine(), Space(), StrongOrEmph(), Image(), Code(), InlineHtml(),
Entity(), EscapedChar())
.addNonNulls(ext(QUOTES) ? new Rule[]{SingleQuoted(), DoubleQuoted(), DoubleAngleQuoted()} : null)
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/org/pegdown/PegDownProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.parboiled.Parboiled;
import org.pegdown.ast.RootNode;
import org.pegdown.plugins.PegDownPlugins;

/**
* A clean and lightweight Markdown-to-HTML filter based on a PEG parser implemented with parboiled.
Expand Down Expand Up @@ -57,16 +58,38 @@ public PegDownProcessor(long maxParsingTimeInMillis) {
* @param options the flags of the extensions to enable as a bitmask
*/
public PegDownProcessor(int options) {
this(options, DEFAULT_MAX_PARSING_TIME);
this(options, DEFAULT_MAX_PARSING_TIME, PegDownPlugins.NONE);
}

/**
* Creates a new processor instance with the given {@link org.pegdown.Extensions} and parsing timeout.
*
* @param options the flags of the extensions to enable as a bitmask
* @param maxParsingTimeInMillis the parsing timeout
*/
public PegDownProcessor(int options, long maxParsingTimeInMillis) {
this(Parboiled.createParser(Parser.class, options, maxParsingTimeInMillis, Parser.DefaultParseRunnerProvider));
this(options, maxParsingTimeInMillis, PegDownPlugins.NONE);
}

/**
* Creates a new processor instance with the given {@link org.pegdown.Extensions} and plugins.
*
* @param options the flags of the extensions to enable as a bitmask
* @param plugins the plugins to use
*/
public PegDownProcessor(int options, PegDownPlugins plugins) {
this(options, DEFAULT_MAX_PARSING_TIME, plugins);
}

/**
* Creates a new processor instance with the given {@link org.pegdown.Extensions}, parsing timeout and plugins.
*
* @param options the flags of the extensions to enable as a bitmask
* @param maxParsingTimeInMillis the parsing timeout
* @param plugins the plugins to use
*/
public PegDownProcessor(int options, long maxParsingTimeInMillis, PegDownPlugins plugins) {
this(Parboiled.createParser(Parser.class, options, maxParsingTimeInMillis, Parser.DefaultParseRunnerProvider, plugins));
}

/**
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/org/pegdown/ToHtmlSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@

import org.parboiled.common.StringUtils;
import org.pegdown.ast.*;
import org.pegdown.plugins.ToHtmlSerializerPlugin;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.TreeMap;

import static org.parboiled.common.Preconditions.checkArgNotNull;
Expand All @@ -36,6 +36,7 @@ public class ToHtmlSerializer implements Visitor {
protected final Map<String, ReferenceNode> references = new HashMap<String, ReferenceNode>();
protected final Map<String, String> abbreviations = new HashMap<String, String>();
protected final LinkRenderer linkRenderer;
protected final List<ToHtmlSerializerPlugin> plugins;

protected TableNode currentTableNode;
protected int currentTableColumn;
Expand All @@ -44,16 +45,26 @@ public class ToHtmlSerializer implements Visitor {
protected Map<String, VerbatimSerializer> verbatimSerializers;

public ToHtmlSerializer(LinkRenderer linkRenderer) {
this(linkRenderer, Collections.<ToHtmlSerializerPlugin>emptyList());
}

public ToHtmlSerializer(LinkRenderer linkRenderer, List<ToHtmlSerializerPlugin> plugins) {
this.linkRenderer = linkRenderer;
this.plugins = plugins;
this.verbatimSerializers = Collections.<String, VerbatimSerializer>singletonMap(VerbatimSerializer.DEFAULT, DefaultVerbatimSerializer.INSTANCE);
}

public ToHtmlSerializer(final LinkRenderer linkRenderer, final Map<String, VerbatimSerializer> verbatimSerializers) {
this(linkRenderer, verbatimSerializers, Collections.<ToHtmlSerializerPlugin>emptyList());
}

public ToHtmlSerializer(final LinkRenderer linkRenderer, final Map<String, VerbatimSerializer> verbatimSerializers, final List<ToHtmlSerializerPlugin> plugins) {
this.linkRenderer = linkRenderer;
this.verbatimSerializers = new HashMap<String, VerbatimSerializer>(verbatimSerializers);
if(!this.verbatimSerializers.containsKey(VerbatimSerializer.DEFAULT)) {
this.verbatimSerializers.put(VerbatimSerializer.DEFAULT, DefaultVerbatimSerializer.INSTANCE);
}
this.plugins = plugins;
}

public String toHtml(RootNode astRoot) {
Expand Down Expand Up @@ -338,8 +349,13 @@ public void visit(SuperNode node) {
}

public void visit(Node node) {
for (ToHtmlSerializerPlugin plugin : plugins) {
if (plugin.visit(node, this, printer)) {
return;
}
}
// override this method for processing custom Node implementations
throw new RuntimeException("Not implemented");
throw new RuntimeException("Don't know how to handle node " + node);
}

// helpers
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/pegdown/plugins/BlockPluginParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.pegdown.plugins;

import org.parboiled.Rule;

/**
* A parser that provides block parser rules for pegdown. A pegdown plugin should implement this, along with
* {@link org.parboiled.BaseParser} or {@link org.pegdown.Parser} if it wants to use the pegdown utilities.
*
* This interface is intended for use with {@link PegDownPlugins.Builder#withPlugin(Class, Object...)}, in order for
* Java plugins to easily be registered with a parser.
*/
public interface BlockPluginParser {
Rule[] blockPluginRules();
}
32 changes: 32 additions & 0 deletions src/main/java/org/pegdown/plugins/InlinePluginParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.pegdown.plugins;

import org.parboiled.Rule;

/**
* A parser that provides inline parser rules for pegdown. A pegdown plugin should implement this, along with
* {@link org.parboiled.BaseParser} or {@link org.pegdown.Parser} if it wants to use the pegdown utilities.
*
* This interface is intended for use with {@link PegDownPlugins.Builder#withPlugin(Class, Object...)}, in order for
* Java plugins to easily be registered with a parser.
*/
public interface InlinePluginParser {
Rule[] inlinePluginRules();
}
Loading