sirthias / pegdown

A pure-Java Markdown processor based on a parboiled PEG parser supporting a number of extensions
http://pegdown.org
Apache License 2.0
1.29k stars 217 forks source link

Fenced code block must be preceded by an empty line #128

Closed thallgren closed 9 years ago

thallgren commented 10 years ago

I'm using a PegDownProcessor created with Extensions.ALL as a replacement for ruby code that used the github-markdown gem. Most things work seamlessly but there's a difference in how fenced code blocks are handled. This text:


Code example

```ruby
a = b
```

formats OK using PegDown and the resulting html contains a <code class="ruby"> element. This text however:


Code example
```ruby
a = b
```

will instead yield <code>ruby e.g. the 'ruby' is no longer a class attribute, instead it becomes visible text in the output.

The only difference between the two examples is that an empty line precedes the fenced block in the first one.

thallgren commented 10 years ago

Some debugging shows that the gets FencedCodeBlock is eaten by the NonLinkInline rule that adds a Code rule.

sirthias commented 10 years ago

Yes, currently a fenced code block must be separated by other block elements with an empty line.

thallgren commented 10 years ago

Any chance that this may change?

sirthias commented 10 years ago

You could submit a PR with a fix... :)

thallgren commented 10 years ago

I know. I actually set out to do just that but realized that it wasn't trivial. First I need to learn new parser technology, then new build technology, and finally test technology that I haven't been using either. I can't motivate the time it would take to do that right now. Someone with the right skill-set can probably solve the problem in a faction of the time I'd need to spend.

sirthias commented 10 years ago

I see, no worries. This is what github is for. I'll keep this ticket open.

takkuumi commented 9 years ago

I want to know how to do it too,i meet the same with @thallgren 's problom

takkuumi commented 9 years ago

@thallgren @sirthias I have solved this problem,i just show my code here.

1.You should add a class in plugins package named CodeParser.java

package org.pegdown.plugins;

import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.support.StringBuilderVar;
import org.pegdown.Parser;
import org.pegdown.ast.VerbatimNode;

/**
 * Created by ling on 2015/3/30.
 */
public class CodeParser extends Parser implements InlinePluginParser {
    private final String TAG = "```";

    public CodeParser() {
        super(ALL, 1000L, DefaultParseRunnerProvider);
    }

    @Override
    public Rule[] inlinePluginRules() {
        return new Rule[]{
                codeInline()
        };
    }

    public Rule codeInline() {
        StringBuilderVar language = new StringBuilderVar();
        StringBuilderVar code = new StringBuilderVar();
        return NodeSequence(
                Sequence(
                        TAG,
                        OneOrMore(
                                TestNot('\n'),
                                BaseParser.ANY,
                                language.append(matchedChar()
                                )
                        ),
                        push(language.getString().trim())
                ),
                Sequence(
                        OneOrMore(
                                TestNot(TAG),
                                BaseParser.ANY,
                                code.append(matchedChar())
                        ),
                        push(code.getString().trim())
                ),
                TAG,
                push(
                        new VerbatimNode(
                                (String) pop(),
                                (String) pop()
                        )
                )
        );
    }
}

2.You need to regist the CodeParser into the plugins.It look like this:

 private static PegDownPlugins plugins;
    private static PegDownProcessor processor;

    static {
        plugins = PegDownPlugins.builder().withPlugin(EmojiParser.class).withPlugin(CodeParser.class).build();
        processor = new PegDownProcessor(Extensions.ALL, plugins);
    }

this is my own main demo

import org.apache.commons.lang3.StringEscapeUtils;
import org.pegdown.Extensions;
import org.pegdown.PegDownProcessor;
import org.pegdown.plugins.CodeParser;
import org.pegdown.plugins.EmojiParser;
import org.pegdown.plugins.PegDownPlugins;

import java.io.UnsupportedEncodingException;

/**
 * Created by ling on 2015/3/26.
 */
public class Test {

    private static PegDownPlugins plugins;
    private static PegDownProcessor processor;

    static {
        plugins = PegDownPlugins.builder().withPlugin(EmojiParser.class).withPlugin(CodeParser.class).build();
        processor = new PegDownProcessor(Extensions.ALL, plugins);
    }

    /**
     * markdown to HTML
     *
     * @param content
     * @return
     */
    public static String markdownToHtml(String content) {
        try {
            content = StringEscapeUtils.unescapeHtml4(content);
            content = processor.markdownToHtml(content);
            return content;
        } catch (Exception e) {
            return content;
        }
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        String md = "1111111111111\n" +
                "``` c\n" +
                "int main(){\n" +
                "   return 0;\n" +
                "}\n" +
                "```";
        System.out.println(StringEscapeUtils.unescapeHtml4(markdownToHtml(new String(md))));
    }
}
sirthias commented 9 years ago

Closing as suggested by @vsch in #181.