├── src ├── test │ ├── resources │ │ ├── pandoc │ │ │ ├── clean.sh │ │ │ ├── convert-to-html.sh │ │ │ ├── convert-to-adoc.sh │ │ │ ├── unorderedlists.md │ │ │ ├── nestedquotes.md │ │ │ ├── quotes.md │ │ │ └── nestednestedquotes.md │ │ ├── nl │ │ │ └── jworks │ │ │ │ └── markdown_to_asciidoc │ │ │ │ ├── lines.feature │ │ │ │ ├── descriptionlists.feature │ │ │ │ ├── paragraphs.feature │ │ │ │ ├── headings.feature │ │ │ │ ├── code.feature │ │ │ │ ├── links.feature │ │ │ │ ├── tables.feature │ │ │ │ ├── lists.feature │ │ │ │ └── markup.feature │ │ ├── testsuite.md │ │ └── testsuite.adoc │ └── java │ │ └── nl │ │ └── jworks │ │ └── markdown_to_asciidoc │ │ ├── RunCukesTest.java │ │ ├── Stepdefs.java │ │ ├── TestSuite.java │ │ └── html │ │ └── TableToAsciiDocTest.java └── main │ └── java │ └── nl │ └── jworks │ └── markdown_to_asciidoc │ ├── code │ └── Linguist.java │ ├── Converter.java │ ├── html │ └── TableToAsciiDoc.java │ └── ToAsciiDocSerializer.java ├── CODE_OF_CONDUCT.adoc ├── .gitignore ├── doc └── cucumber.txt ├── LICENCE ├── .github ├── dependabot.yml └── workflows │ ├── pr.yml │ ├── build.yml │ └── release.yml ├── README.adoc └── pom.xml /src/test/resources/pandoc/clean.sh: -------------------------------------------------------------------------------- 1 | rm *.adoc 2 | rm *.html -------------------------------------------------------------------------------- /src/test/resources/pandoc/convert-to-html.sh: -------------------------------------------------------------------------------- 1 | asciidoctor unorderedlists.adoc -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.adoc: -------------------------------------------------------------------------------- 1 | = Contributor Covenant Code of Conduct 2 | 3 | Simply: be nice. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.classpath 2 | /.gradle/ 3 | /.idea/ 4 | /.project 5 | /.settings/ 6 | /build/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /src/test/resources/pandoc/convert-to-adoc.sh: -------------------------------------------------------------------------------- 1 | pandoc -s -S unorderedlists.md -t asciidoc -o unorderedlists.adoc -------------------------------------------------------------------------------- /src/test/resources/pandoc/unorderedlists.md: -------------------------------------------------------------------------------- 1 | # test 2 | 3 | * Item 1 4 | * Item 1_1 5 | * Item 1_2 6 | 7 | -------------------------------------------------------------------------------- /doc/cucumber.txt: -------------------------------------------------------------------------------- 1 | == Links 2 | 3 | https://github.com/cucumber/cucumber-java-skeleton 4 | https://github.com/asciidoctor/asciidoctor/blob/master/features/open_block.feature 5 | 6 | == Tips 7 | 8 | To run only a specific test, add this to IntelliJ: 9 | 10 | -Dcucumber.options="--tags @tables" -------------------------------------------------------------------------------- /src/test/java/nl/jworks/markdown_to_asciidoc/RunCukesTest.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions 9 | public class RunCukesTest { 10 | } 11 | -------------------------------------------------------------------------------- /src/test/resources/pandoc/nestedquotes.md: -------------------------------------------------------------------------------- 1 | 2 | = Nested quotes 3 | 4 | Small test for nested quotes 5 | 6 | > > What's new? 7 | > 8 | > I've got Markdown in my AsciiDoc! 9 | > 10 | > > Like what? 11 | > 12 | > * Blockquotes 13 | > * Headings 14 | > * Fenced code blocks 15 | > 16 | > > Is there more? 17 | > 18 | > Yep. AsciiDoc and Markdown share a lot of common syntax already. 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/pandoc/quotes.md: -------------------------------------------------------------------------------- 1 | = Quotes 2 | 3 | Small test for quotes 4 | 5 | 6 | > Blockquotes are very handy in email to emulate reply text. 7 | > This line is part of the same quote. 8 | 9 | Quote break. 10 | 11 | > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. 12 | -------------------------------------------------------------------------------- /src/test/resources/pandoc/nestednestedquotes.md: -------------------------------------------------------------------------------- 1 | 2 | = Nested quotes 3 | 4 | Small test for nested quotes 5 | 6 | > > > Hello World! 7 | > 8 | > > What's new? 9 | > 10 | > I've got Markdown in my AsciiDoc! 11 | > 12 | > > > I think so! 13 | > 14 | > > Like what? 15 | > 16 | > * Blockquotes 17 | > * Headings 18 | > * Fenced code blocks 19 | > 20 | > > Is there more? 21 | > 22 | > Yep. AsciiDoc and Markdown share a lot of common syntax already. 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/nl/jworks/markdown_to_asciidoc/code/Linguist.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc.code; 2 | 3 | 4 | /** 5 | * A really basic way of detecting code types. 6 | */ 7 | public class Linguist { 8 | 9 | public String detectLanguage(String text) { 10 | if (text.startsWith("<")) { 11 | return "html"; 12 | } else if (text.endsWith(";")) { 13 | return "java"; 14 | } else { 15 | return "groovy"; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/lines.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @lines 3 | Feature: Lines 4 | In order to group content 5 | As a writer 6 | I want to be able to create horizontal lines 7 | 8 | Scenario: Create 4 horizontal lines 9 | Given the Markdown source 10 | """ 11 | --- 12 | 13 | - - - 14 | 15 | *** 16 | 17 | * * * 18 | """ 19 | When it is converted to AsciiDoc 20 | Then the result should match the AsciiDoc source 21 | """ 22 | ''' 23 | ''' 24 | ''' 25 | ''' 26 | """ 27 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Erik Pragt 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | # Maintain dependencies for GitHub Actions 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Verify PR 2 | on: 3 | pull_request: 4 | branches: 5 | - "**" 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | java-version: [ 17, 21 ] 12 | runs-on: [ ubuntu-latest, macos-latest, windows-latest ] 13 | name: Jdk ${{ matrix.java-version }}, os ${{ matrix.runs-on }} 14 | runs-on: ${{ matrix.runs-on }} 15 | steps: 16 | - uses: actions/checkout@v5 17 | - name: Set up JDK ${{ matrix.java-version }} 18 | uses: actions/setup-java@v4 19 | with: 20 | java-version: ${{ matrix.java-version }} 21 | distribution: 'adopt' 22 | cache: maven 23 | - name: Build with Maven 24 | run: mvn --batch-mode verify -Pcoverage 25 | - name: Upload coverage reports to Codecov 26 | uses: codecov/codecov-action@v5 27 | env: 28 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} -------------------------------------------------------------------------------- /src/test/java/nl/jworks/markdown_to_asciidoc/Stepdefs.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc; 2 | 3 | 4 | import io.cucumber.java.en.Given; 5 | import io.cucumber.java.en.Then; 6 | import io.cucumber.java.en.When; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class Stepdefs { 11 | 12 | private String markdown; 13 | private String asciiDoc; 14 | 15 | @Given("^the Markdown source$") 16 | public void the_markdown_source(String markdown) { 17 | if (markdown.contains("{sp}")) { 18 | this.markdown = markdown.replaceAll("\\{sp}", " "); 19 | } else { 20 | this.markdown = markdown; 21 | } 22 | } 23 | 24 | @When("^it is converted to AsciiDoc") 25 | public void it_is_converted_to_asciidoc() { 26 | this.asciiDoc = Converter.convertMarkdownToAsciiDoc(markdown.replaceAll("\r\n", "\n")); 27 | } 28 | 29 | @Then("^the result should match the AsciiDoc source$") 30 | public void the_result_should_match_the_asciidoc_source(String result) { 31 | assertEquals(result.replaceAll("\r\n", "\n"), asciiDoc.replaceAll("\r\n", "\n")); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/nl/jworks/markdown_to_asciidoc/TestSuite.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.junit.Ignore; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.OutputStreamWriter; 11 | import java.net.URL; 12 | import java.nio.charset.StandardCharsets; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.fail; 16 | 17 | /** 18 | * Converts the testsuite from https://github.com/karlcow/markdown-testsuite (run cat-all.py) to asciidoc, checks the output 19 | */ 20 | public class TestSuite { 21 | @Ignore 22 | @Test 23 | public void test() { 24 | String markDown = readToString("testsuite.md"); 25 | String asciiDoc = Converter.convertMarkdownToAsciiDoc(markDown); 26 | assertEquals(readToString("testsuite.adoc"), asciiDoc); 27 | } 28 | 29 | private String readToString(String resourceName) { 30 | URL url = getClass().getResource("/" + resourceName); 31 | try { 32 | return IOUtils.toString(url.openStream()); 33 | } catch (IOException e) { 34 | fail(); 35 | return null; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/descriptionlists.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @descriptionlists 3 | Feature: Description lists 4 | In order to describe terms 5 | As a writer 6 | I want to be able to create description lists 7 | 8 | Scenario: Render a description list 9 | Given the Markdown source 10 | """ 11 | Apple 12 | : Pomaceous fruit of plants of the genus Malus in 13 | the family Rosaceae. 14 | """ 15 | When it is converted to AsciiDoc 16 | Then the result should match the AsciiDoc source 17 | """ 18 | Apple:: 19 | Pomaceous fruit of plants of the genus Malus in 20 | the family Rosaceae. 21 | """ 22 | 23 | Scenario: Render a multiple description lists 24 | Given the Markdown source 25 | """ 26 | Apple 27 | : Pomaceous fruit of plants of the genus Malus in 28 | the family Rosaceae. 29 | 30 | Orange 31 | : The fruit of an evergreen tree of the genus Citrus. 32 | """ 33 | When it is converted to AsciiDoc 34 | Then the result should match the AsciiDoc source 35 | """ 36 | Apple:: 37 | Pomaceous fruit of plants of the genus Malus in 38 | the family Rosaceae. 39 | Orange:: 40 | The fruit of an evergreen tree of the genus Citrus. 41 | """ 42 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Snapshots 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: write 11 | packages: write 12 | steps: 13 | - uses: actions/checkout@v5 14 | - name: Set up JDK 17 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '17' 18 | distribution: 'adopt' 19 | cache: maven 20 | - name: Deploy to Github packages 21 | run: mvn --batch-mode clean deploy -Pgithub,coverage 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | - name: Upload coverage reports to Codecov 25 | uses: codecov/codecov-action@v5 26 | env: 27 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 28 | - name: Set up Java for publishing to Maven Central Repository 29 | uses: actions/setup-java@v4 30 | with: 31 | java-version: '17' 32 | distribution: 'temurin' 33 | cache: maven 34 | server-id: ossrh 35 | server-username: MAVEN_USERNAME 36 | server-password: MAVEN_PASSWORD 37 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 38 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 39 | - name: Publish to the Maven Central Repository 40 | run: mvn --batch-mode clean deploy -Possrh 41 | env: 42 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 43 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 44 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 45 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/paragraphs.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @paragraphs 3 | Feature: Paragraphs 4 | In order to write normal content 5 | As a writer 6 | I want to be able to create discrete paragraphs 7 | 8 | Scenario: Render a paragraph with a single line 9 | Given the Markdown source 10 | """ 11 | A paragraph with a single line. 12 | """ 13 | When it is converted to AsciiDoc 14 | Then the result should match the AsciiDoc source 15 | """ 16 | A paragraph with a single line. 17 | """ 18 | 19 | Scenario: Render a paragraph with multiple lines 20 | Given the Markdown source 21 | """ 22 | First line of paragraph. 23 | Second line of paragraph. 24 | """ 25 | When it is converted to AsciiDoc 26 | Then the result should match the AsciiDoc source 27 | """ 28 | First line of paragraph. 29 | Second line of paragraph. 30 | """ 31 | 32 | Scenario: Render multiple paragraphs seperated by a blank line 33 | Given the Markdown source 34 | """ 35 | First paragraph. 36 | 37 | Second paragraph. 38 | """ 39 | When it is converted to AsciiDoc 40 | Then the result should match the AsciiDoc source 41 | """ 42 | First paragraph. 43 | 44 | Second paragraph. 45 | """ 46 | 47 | Scenario: Render special characters as is 48 | Given the Markdown source 49 | """ 50 | This is an example: `Provider>` 51 | """ 52 | When it is converted to AsciiDoc 53 | Then the result should match the AsciiDoc source 54 | """ 55 | This is an example: `Provider>` 56 | """ 57 | -------------------------------------------------------------------------------- /src/main/java/nl/jworks/markdown_to_asciidoc/Converter.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc; 2 | 3 | import org.pegdown.Extensions; 4 | import org.pegdown.PegDownProcessor; 5 | import org.pegdown.ast.RootNode; 6 | 7 | import java.io.*; 8 | 9 | public class Converter { 10 | public static void main(String[] args) { 11 | if (args.length != 1) { 12 | System.err.println("markdown_to_asciidoc: Please specify a file to convert"); 13 | return; 14 | } 15 | 16 | File input = new File(args[0]); 17 | if (!input.exists()) { 18 | System.err.println("markdown_to_asciidoc: Cannot find the specified file to convert"); 19 | return; 20 | } 21 | 22 | try (BufferedReader reader = new BufferedReader(new FileReader(input))) { 23 | StringBuilder buffer = new StringBuilder(); 24 | String line; 25 | while ((line = reader.readLine()) != null) { 26 | buffer.append(line).append("\n"); 27 | } 28 | 29 | reader.close(); 30 | System.out.println(convertMarkdownToAsciiDoc(buffer.toString().trim())); 31 | } catch (IOException e) { 32 | System.err.println("markdown_to_asciidoc: An error occurred while reading the input file"); 33 | } 34 | } 35 | 36 | public static String convertMarkdownToAsciiDoc(String markdown) { 37 | PegDownProcessor processor = new PegDownProcessor(Extensions.ALL); 38 | char[] markDown = markdown.toCharArray(); 39 | RootNode rootNode = processor.parseMarkdown(markDown); 40 | return new ToAsciiDocSerializer(rootNode, markdown).toAsciiDoc(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Markdown to AsciiDoc converter 2 | :library-version: 2.0.1 3 | 4 | image:https://github.com/markdown-asciidoc/markdown-to-asciidoc/actions/workflows/build.yml/badge.svg[Build Status] 5 | image:https://github.com/markdown-asciidoc/markdown-to-asciidoc/actions/workflows/release.yml/badge.svg[Publish Status] 6 | 7 | image:https://badges.gitter.im/Join%20Chat.svg[link="https://gitter.im/markdown-asciidoc/markdown-to-asciidoc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] 8 | image:https://maven-badges.herokuapp.com/maven-central/io.github.markdown-asciidoc/markdown-to-asciidoc/badge.svg?style={style}[link=https://maven-badges.herokuapp.com/maven-central/io.github.markdown-asciidoc/markdown-to-asciidoc] 9 | image:https://img.shields.io/github/license/apache/maven.svg?label=License[link=LICENCE] 10 | 11 | A small, lightweight converter for Markdown to http://www.asciidoc.org[AsciiDoc] written in Java, and based on https://github.com/sirthias/pegdown[Pegdown]. 12 | 13 | Uses https://github.com/markdown-asciidoc/markdown-to-asciidoc/tree/master/src/test/resources/nl/jworks/markdown_to_asciidoc[Cucumber tests] as a reusable 14 | set to test conversion between Markdown and AsciiDoc. 15 | 16 | == Features 17 | 18 | Currently, the following are supported by the Markdown to AsciiDoc converter: 19 | 20 | * Code blocks 21 | * Definition lists 22 | * Headings 23 | * Lines 24 | * Links 25 | * Lists (ordered, unordered, nested, mixed) 26 | * Basic markup (bold, italic, monospaced, etc) 27 | * Tables (including alignments) 28 | 29 | Also, currently known *not* supported items are: 30 | 31 | * Table cell spanning 32 | 33 | == Usage 34 | 35 | Add the following dependency to your project: 36 | 37 | `io.github.markdown-asciidoc:markdown-to-asciidoc:{library-version}` 38 | 39 | To use the library, call the following: 40 | 41 | `assertEquals("= Title", Converter.convertMarkdownToAsciiDoc("# Title"));` 42 | 43 | The Converter converts all Markdown input to AsciiDoc. 44 | 45 | == Uses 46 | 47 | Currently, the conversion library is used in the https://plugins.jetbrains.com/plugin/7391[IntelliJ AsciiDoc Plugin] to seamlessly convert Markdown to AsciiDoc. 48 | 49 | If you know of any projects using the library, please let me know on Twitter: http://www.twitter.com/epragt[@epragt]. 50 | 51 | == Bugs 52 | 53 | If you find a bug or a missing feature, please report it the https://github.com/markdown-asciidoc/markdown-to-asciidoc/issues[Github Issue Tracker]. 54 | -------------------------------------------------------------------------------- /src/main/java/nl/jworks/markdown_to_asciidoc/html/TableToAsciiDoc.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc.html; 2 | 3 | import org.jsoup.Jsoup; 4 | import org.jsoup.nodes.Document; 5 | import org.jsoup.nodes.Element; 6 | import org.jsoup.select.Elements; 7 | 8 | public class TableToAsciiDoc { 9 | public static String convert(String html) { 10 | if (!html.startsWith("tag. 31 | ``` 32 | """ 33 | When it is converted to AsciiDoc 34 | Then the result should match the AsciiDoc source 35 | """ 36 | ---- 37 | No language indicated, so no syntax highlighting. 38 | But let's throw in a tag. 39 | ---- 40 | """ 41 | 42 | Scenario: Render a javascript code block 43 | Given the Markdown source 44 | """ 45 | ```javascript 46 | var s = "JavaScript syntax highlighting"; 47 | alert(s); 48 | ``` 49 | """ 50 | When it is converted to AsciiDoc 51 | Then the result should match the AsciiDoc source 52 | """ 53 | [source,javascript] 54 | ---- 55 | var s = "JavaScript syntax highlighting"; 56 | alert(s); 57 | ---- 58 | """ 59 | 60 | Scenario: Render a python code block 61 | Given the Markdown source 62 | """ 63 | ```python 64 | s = "Python syntax highlighting" 65 | print s 66 | ``` 67 | """ 68 | When it is converted to AsciiDoc 69 | Then the result should match the AsciiDoc source 70 | """ 71 | [source,python] 72 | ---- 73 | s = "Python syntax highlighting" 74 | print s 75 | ---- 76 | """ 77 | 78 | Scenario: Render an indented code block 79 | Given the Markdown source 80 | """ 81 | $ gem install asciidoctor 82 | """ 83 | When it is converted to AsciiDoc 84 | Then the result should match the AsciiDoc source 85 | """ 86 | ---- 87 | $ gem install asciidoctor 88 | ---- 89 | """ 90 | 91 | Scenario: Render code block adjacent to preceding paragraph 92 | Given the Markdown source 93 | """ 94 | Here's an example: 95 | ```javascript 96 | var s = "JavaScript syntax highlighting"; 97 | alert(s); 98 | ``` 99 | """ 100 | When it is converted to AsciiDoc 101 | Then the result should match the AsciiDoc source 102 | """ 103 | Here's an example: 104 | 105 | [source,javascript] 106 | ---- 107 | var s = "JavaScript syntax highlighting"; 108 | alert(s); 109 | ---- 110 | """ 111 | 112 | Scenario: Render inline code 113 | Given the Markdown source 114 | """ 115 | We defined the `add` function to 116 | """ 117 | When it is converted to AsciiDoc 118 | Then the result should match the AsciiDoc source 119 | """ 120 | We defined the `add` function to 121 | """ 122 | 123 | Scenario: Render no extra lines 124 | Given the Markdown source 125 | """ 126 | foo 127 | 128 | ```kotlin 129 | println("bar") 130 | ``` 131 | 132 | baz 133 | 134 | ```kotlin 135 | println("bam") 136 | ``` 137 | """ 138 | When it is converted to AsciiDoc 139 | Then the result should match the AsciiDoc source 140 | """ 141 | foo 142 | 143 | [source,kotlin] 144 | ---- 145 | println("bar") 146 | ---- 147 | 148 | baz 149 | 150 | [source,kotlin] 151 | ---- 152 | println("bam") 153 | ---- 154 | """ 155 | 156 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/links.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @links 3 | Feature: Links 4 | In order to group content 5 | As a writer 6 | I want to be able to create links 7 | 8 | Scenario: Render an implicit inline link 9 | Given the Markdown source 10 | """ 11 | Use [http://example.com](http://example.com) for sample links in documentation. 12 | """ 13 | When it is converted to AsciiDoc 14 | Then the result should match the AsciiDoc source 15 | """ 16 | Use http://example.com for sample links in documentation. 17 | """ 18 | 19 | Scenario: Render an inline link 20 | Given the Markdown source 21 | """ 22 | This is [an example](http://example.com/) inline link. 23 | """ 24 | When it is converted to AsciiDoc 25 | Then the result should match the AsciiDoc source 26 | """ 27 | This is http://example.com/[an example] inline link. 28 | """ 29 | 30 | Scenario: Render linked text with comma 31 | Given the Markdown source 32 | """ 33 | This is [a very, very cool](http://example.com/) inline link. 34 | """ 35 | When it is converted to AsciiDoc 36 | Then the result should match the AsciiDoc source 37 | """ 38 | This is http://example.com/["a very, very cool"] inline link. 39 | """ 40 | 41 | Scenario: Render a reference style link with link definition 42 | Given the Markdown source 43 | """ 44 | The [syntax page] [s] provides complete, detailed documentation for 45 | 46 | [s]: /projects/markdown/syntax "Markdown Syntax" 47 | """ 48 | When it is converted to AsciiDoc 49 | Then the result should match the AsciiDoc source 50 | """ 51 | The link:/projects/markdown/syntax[syntax page] provides complete, detailed documentation for 52 | """ 53 | 54 | Scenario: Render a reference style link with link text 55 | Given the Markdown source 56 | """ 57 | The [syntax page] provides complete, detailed documentation for 58 | 59 | [syntax page]: http://www.syntaxpage.com 60 | """ 61 | When it is converted to AsciiDoc 62 | Then the result should match the AsciiDoc source 63 | """ 64 | The http://www.syntaxpage.com[syntax page] provides complete, detailed documentation for 65 | """ 66 | 67 | Scenario: Render an internal link using the cross reference syntax 68 | Given the Markdown source 69 | """ 70 | Refer to [Quick start](#quick-start) to learn how to get started. 71 | """ 72 | When it is converted to AsciiDoc 73 | Then the result should match the AsciiDoc source 74 | """ 75 | Refer to <> to learn how to get started. 76 | """ 77 | 78 | Scenario: Render an reference style image 79 | Given the Markdown source 80 | """ 81 | ![Alt text][logo] 82 | 83 | [logo]: images/icons/home.png 84 | """ 85 | When it is converted to AsciiDoc 86 | Then the result should match the AsciiDoc source 87 | """ 88 | image:images/icons/home.png[Alt text] 89 | """ 90 | 91 | Scenario: Render an inline image with parameters 92 | Given the Markdown source 93 | """ 94 | ![Alt text](images/icons/home.png) 95 | 96 | ![Alt text](images/icons/home.png?width=100) 97 | """ 98 | When it is converted to AsciiDoc 99 | Then the result should match the AsciiDoc source 100 | """ 101 | image:images/icons/home.png[Alt text] 102 | 103 | image:images/icons/home.png?width=100[Alt text] 104 | """ 105 | 106 | Scenario: Render an inline image with comma in alt text 107 | Given the Markdown source 108 | """ 109 | ![Alt,text](images/icons/home.png) 110 | """ 111 | When it is converted to AsciiDoc 112 | Then the result should match the AsciiDoc source 113 | """ 114 | image:images/icons/home.png["Alt,text"] 115 | """ 116 | 117 | Scenario: Render a hyperlinked inline image with alt text 118 | Given the Markdown source 119 | """ 120 | [![Build Status](https://travis-ci.org/asciidoctor/asciidoctor.png)](https://travis-ci.org/asciidoctor/asciidoctor) 121 | """ 122 | When it is converted to AsciiDoc 123 | Then the result should match the AsciiDoc source 124 | """ 125 | image:https://travis-ci.org/asciidoctor/asciidoctor.png[Build Status,link=https://travis-ci.org/asciidoctor/asciidoctor] 126 | """ 127 | 128 | #failing 129 | #Scenario: Render a hyperlinked inline image with no alt text 130 | # Given the Markdown source 131 | # """ 132 | # [![](https://travis-ci.org/asciidoctor/asciidoctor.png)](https://travis-ci.org/asciidoctor/asciidoctor) 133 | # """ 134 | # When it is converted to AsciiDoc 135 | # Then the result should match the AsciiDoc source 136 | # """ 137 | # image:https://travis-ci.org/asciidoctor/asciidoctor.png[link=https://travis-ci.org/asciidoctor/asciidoctor] 138 | # """ 139 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/tables.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @tables 3 | Feature: Tables 4 | In order to group content 5 | As a writer 6 | I want to be able to create tables 7 | 8 | Scenario: Render a table 9 | Given the Markdown source 10 | """ 11 | | Name of Column 1 | Name of Column 2| 12 | | ---------------- | --------------- | 13 | | Cell in column 1, row 1 | Cell in column 2, row 1| 14 | | Cell in column 1, row 2 | Cell in column 2, row 2| 15 | """ 16 | When it is converted to AsciiDoc 17 | Then the result should match the AsciiDoc source 18 | """ 19 | |=== 20 | |Name of Column 1 |Name of Column 2 21 | 22 | |Cell in column 1, row 1 |Cell in column 2, row 1 23 | |Cell in column 1, row 2 |Cell in column 2, row 2 24 | |=== 25 | """ 26 | 27 | Scenario: Render a table under a paragraph 28 | Given the Markdown source 29 | """ 30 | # Step 31 | 32 | Lorem Ipsum etc. 33 | 34 | - `sample.yaml`: sample file to do some stuff with 35 | 36 | | Name of Column 1 | Name of Column 2| 37 | | ---------------- | --------------- | 38 | | Cell in column 1, row 1 | Cell in column 2, row 1| 39 | | Cell in column 1, row 2 | Cell in column 2, row 2| 40 | """ 41 | When it is converted to AsciiDoc 42 | Then the result should match the AsciiDoc source 43 | """ 44 | = Step 45 | 46 | Lorem Ipsum etc. 47 | 48 | * `sample.yaml`: sample file to do some stuff with 49 | |=== 50 | |Name of Column 1 |Name of Column 2 51 | 52 | |Cell in column 1, row 1 |Cell in column 2, row 1 53 | |Cell in column 1, row 2 |Cell in column 2, row 2 54 | |=== 55 | """ 56 | 57 | # NOTE we are still getting trailing space at the end of lines 58 | Scenario: Leave a trailing space at the end of each adjacent cell 59 | Given the Markdown source 60 | """ 61 | | Browser | Tablet | Smartphone | 62 | | ------- | ------ | ---------- | 63 | | Safari 5.1+| iPad 2+ | iOS 6+ | 64 | """ 65 | When it is converted to AsciiDoc 66 | Then the result should match the AsciiDoc source 67 | """ 68 | |=== 69 | |Browser |Tablet |Smartphone 70 | 71 | |Safari 5.1+ |iPad 2+ |iOS 6+ 72 | |=== 73 | """ 74 | 75 | Scenario: Render a table with left, center and right align columns 76 | Given the Markdown source 77 | """ 78 | | Tables | Are | Cool| 79 | | ------------- |:-------------:| ----:| 80 | | col 3 is | right-aligned | $1600| 81 | | col 2 is | centered | $12| 82 | | zebra stripes | are neat | $1| 83 | """ 84 | When it is converted to AsciiDoc 85 | Then the result should match the AsciiDoc source 86 | """ 87 | [cols="<,^,>"] 88 | |=== 89 | |Tables |Are |Cool 90 | 91 | |col 3 is |right-aligned |$1600 92 | |col 2 is |centered |$12 93 | |zebra stripes |are neat |$1 94 | |=== 95 | """ 96 | 97 | Scenario: Render a markdown HTML table 98 | Given the Markdown source 99 | """ 100 | Care must be taken with slashes when specifying both the base URL and the relative URL as trailing and leading slashes have significant meaning. The following table illustrates the resolution of different types of URLs. 101 | 102 | 103 | 104 | 105 | 106 |
BaseNavigating ToResult
http://myapp.com/abchttp://myapp.com/abc
http://myapp.comabchttp://myapp.comabc
107 | 108 | It is usually most desirable to define your base urls with trailing slashes and not to use leading slashes on relative URLs. 109 | """ 110 | When it is converted to AsciiDoc 111 | Then the result should match the AsciiDoc source 112 | """ 113 | Care must be taken with slashes when specifying both the base URL and the relative URL as trailing and leading slashes have significant meaning. The following table illustrates the resolution of different types of URLs. 114 | 115 | |=== 116 | |Base |Navigating To |Result 117 | 118 | |http://myapp.com/ |abc |http://myapp.com/abc 119 | |http://myapp.com |abc |http://myapp.comabc 120 | |=== 121 | 122 | It is usually most desirable to define your base urls with trailing slashes and not to use leading slashes on relative URLs. 123 | """ 124 | 125 | # @table 126 | # Scenario: Render a table with left, center and right align columns 127 | # Given the Markdown source 128 | # """ 129 | # | | Grouping || 130 | # | First Header | Second Header | Third Header | 131 | # | ------------ | :-----------: | -----------: | 132 | # | Content | *Long Cell* || 133 | # | Content | **Cell** | Cell | 134 | # | New section | More | Data | 135 | # """ 136 | # When it is converted to AsciiDoc 137 | # Then the result should match the AsciiDoc source 138 | # """ 139 | # [cols="<,^,>"] 140 | # |=== 141 | # | 2+| Grouping 142 | # 143 | # | Content 2+| _Long Cell_ 144 | # | Content | *Cell* | Cell 145 | # | New section | More | Data 146 | # |=== 147 | # """ 148 | 149 | -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/lists.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @lists 3 | Feature: Lists 4 | In order to group content 5 | As a writer 6 | I want to be able to create lists 7 | 8 | Scenario: Render an unordered list 9 | Given the Markdown source 10 | """ 11 | * Item 1 12 | * Item 2 13 | * Item 3 14 | """ 15 | When it is converted to AsciiDoc 16 | Then the result should match the AsciiDoc source 17 | """ 18 | * Item 1 19 | * Item 2 20 | * Item 3 21 | """ 22 | 23 | Scenario: Render an unordered list of paragraphs 24 | Given the Markdown source 25 | """ 26 | * Paragraph 1 27 | 28 | * Paragraph 2 29 | """ 30 | When it is converted to AsciiDoc 31 | Then the result should match the AsciiDoc source 32 | """ 33 | * Paragraph 1 34 | 35 | * Paragraph 2 36 | """ 37 | 38 | Scenario: Render an unordered nested list 39 | Given the Markdown source 40 | """ 41 | * Item 1 42 | * Item 1_1 43 | * Item 1_2 44 | """ 45 | When it is converted to AsciiDoc 46 | Then the result should match the AsciiDoc source 47 | """ 48 | * Item 1 49 | ** Item 1_1 50 | ** Item 1_2 51 | """ 52 | 53 | Scenario: Render an ordered list 54 | Given the Markdown source 55 | """ 56 | 1. Item 1 57 | 1. Item 2 58 | 1. Item 3 59 | """ 60 | When it is converted to AsciiDoc 61 | Then the result should match the AsciiDoc source 62 | """ 63 | . Item 1 64 | . Item 2 65 | . Item 3 66 | """ 67 | 68 | Scenario: Render a nested ordered list 69 | Given the Markdown source 70 | """ 71 | 1. Item 1 72 | 1. Item 1.1 73 | 1. Item 1.2 74 | """ 75 | When it is converted to AsciiDoc 76 | Then the result should match the AsciiDoc source 77 | """ 78 | . Item 1 79 | .. Item 1.1 80 | .. Item 1.2 81 | """ 82 | 83 | Scenario: Render a nested ordered list combined with unordered list 84 | Given the Markdown source 85 | """ 86 | 1. Item 1 87 | 1. Item 11 88 | * bullet 111 89 | * bullet 112 90 | * bullet 1121 91 | 1. Item 11211 92 | 1. Item 12 93 | 1. Item 2 94 | """ 95 | When it is converted to AsciiDoc 96 | Then the result should match the AsciiDoc source 97 | """ 98 | . Item 1 99 | .. Item 11 100 | *** bullet 111 101 | *** bullet 112 102 | **** bullet 1121 103 | ..... Item 11211 104 | .. Item 12 105 | . Item 2 106 | """ 107 | 108 | Scenario: Render an ordered list combined with a nested unordered list 109 | Given the Markdown source 110 | """ 111 | 1. Item 1 112 | 113 | 2. Item 2 114 | 115 | * Subitem of Item 2 116 | 117 | 3. Item 3 118 | """ 119 | When it is converted to AsciiDoc 120 | Then the result should match the AsciiDoc source 121 | """ 122 | . Item 1 123 | 124 | . Item 2 125 | 126 | ** Subitem of Item 2 127 | 128 | . Item 3 129 | """ 130 | 131 | Scenario: Render an ordered list of paragraphs 132 | Given the Markdown source 133 | """ 134 | . Paragraph 1 135 | 136 | . Paragraph 2 137 | """ 138 | When it is converted to AsciiDoc 139 | Then the result should match the AsciiDoc source 140 | """ 141 | . Paragraph 1 142 | 143 | . Paragraph 2 144 | """ 145 | 146 | Scenario: Render an unordered list with a link 147 | Given the Markdown source 148 | """ 149 | There is a Maven example project available. 150 | 151 | * [http://github.com/geb/geb-example-maven](https://github.com/geb/geb-example-maven) 152 | """ 153 | When it is converted to AsciiDoc 154 | Then the result should match the AsciiDoc source 155 | """ 156 | There is a Maven example project available. 157 | 158 | * https://github.com/geb/geb-example-maven[http://github.com/geb/geb-example-maven] 159 | """ 160 | 161 | Scenario: Render a list item that ends with a blank 162 | Given the Markdown source 163 | """ 164 | -{sp} 165 | """ 166 | When it is converted to AsciiDoc 167 | Then the result should match the AsciiDoc source 168 | """ 169 | * 170 | """ 171 | 172 | #@@knownissue This doesn't work. Item 4 is contains 3 para nodes instead of a code block 173 | # Scenario: Render 4 numbered items with a code block 174 | # Given the Markdown source 175 | # """ 176 | # 1. Use the `browser` object explicitly (made available by the testing adapters) 177 | # 2. Use the page instance returned by the `to()` and `at()` methods instead of calling through the browser 178 | # 3. Use methods on the `Page` classes instead of the `content {}` block and dynamic properties 179 | # 4. If you need to use content definition options like `required:` and `wait:` then you can still reference content elements defined using the DSL in methods on `Page` and `Module` classes as usual, e.g.: 180 | # 181 | # static content = { 182 | # async(wait: true) { $(".async") } 183 | # } 184 | # 185 | # String asyncText() { 186 | # async.text() // Wait here for the async definition to return a non-empty Navigator... 187 | # } 188 | # Using this “typed” style is not an all or nothing proposition. 189 | # """ 190 | # When it is converted to AsciiDoc 191 | # Then the result should match the AsciiDoc source 192 | # """ 193 | # . Use the `browser` object explicitly (made available by the testing adapters) 194 | # . Use the page instance returned by the `to()` and `at()` methods instead of calling through the browser 195 | # . Use methods on the `Page` classes instead of the `content {}` block and dynamic properties 196 | # . If you need to use content definition options like `required:` and `wait:` then you can still reference content elements defined using the DSL in methods on `Page` and `Module` classes as usual, e.g.: 197 | # 198 | # static content = { 199 | # async(wait: true) { $(".async") } 200 | # } 201 | # 202 | # String asyncText() { 203 | # async.text() // Wait here for the async definition to return a non-empty Navigator... 204 | # } 205 | # 206 | # Using this “typed” style is not an all or nothing proposition. 207 | # """ 208 | -------------------------------------------------------------------------------- /src/test/java/nl/jworks/markdown_to_asciidoc/html/TableToAsciiDocTest.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc.html; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class TableToAsciiDocTest { 8 | 9 | @Test 10 | public void convertTable() { 11 | String html = """ 12 | 13 | 14 | 15 | 16 |
BaseNavigating ToResult
http://myapp.com/abchttp://myapp.com/abc
http://myapp.comabchttp://myapp.comabc
17 | """; 18 | 19 | String expected = """ 20 | |=== 21 | |Base |Navigating To |Result 22 | 23 | |http://myapp.com/ |abc |http://myapp.com/abc 24 | |http://myapp.com |abc |http://myapp.comabc 25 | |=== 26 | """; 27 | 28 | String actual = TableToAsciiDoc.convert(html); 29 | 30 | assertEquals(expected, actual); 31 | 32 | } 33 | 34 | @Test 35 | public void convertTableWithCode() { 36 | String html = """ 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | \s 48 |
Case SensitiveCase InsensitiveDescription
startsWithiStartsWithMatches values that start with the given value
containsiContainsMatches values that contain the given value anywhere
endsWithiEndsWithMatches values that end with the given value
containsWordiContainsWordMatches values that contain the given value surrounded by either whitespace or the beginning or end of the value
notStartsWithiNotStartsWithMatches values that DO NOT start with the given value
notContainsiNotContainsMatches values that DO NOT contain the given value anywhere
notEndsWithiNotEndsWithMatches values that DO NOT end with the given value
notContainsWordiNotContainsWordMatches values that DO NOT contain the given value surrounded by either whitespace or the beginning or end of the value
"""; 49 | 50 | String expected = """ 51 | |=== 52 | |Case Sensitive |Case Insensitive |Description 53 | 54 | |`startsWith` |`iStartsWith` |Matches values that start with the given value 55 | |`contains` |`iContains` |Matches values that contain the given value anywhere 56 | |`endsWith` |`iEndsWith` |Matches values that end with the given value 57 | |`containsWord` |`iContainsWord` |Matches values that contain the given value surrounded by either whitespace or the beginning or end of the value 58 | |`notStartsWith` |`iNotStartsWith` |Matches values that DO NOT start with the given value 59 | |`notContains` |`iNotContains` |Matches values that DO NOT contain the given value anywhere 60 | |`notEndsWith` |`iNotEndsWith` |Matches values that DO NOT end with the given value 61 | |`notContainsWord` |`iNotContainsWord` |Matches values that DO NOT contain the given value surrounded by either whitespace or the beginning or end of the value 62 | |=== 63 | """; 64 | 65 | String actual = TableToAsciiDoc.convert(html); 66 | 67 | assertEquals(expected, actual); 68 | 69 | } 70 | 71 | @Test 72 | public void convertTableWithLinks() { 73 | String html = """ 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
Short NameDriver
htmlunitorg.openqa.selenium.htmlunit.HtmlUnitDriver
firefoxorg.openqa.selenium.firefox.FirefoxDriver
ieorg.openqa.selenium.ie.InternetExplorerDriver
chromeorg.openqa.selenium.chrome.ChromeDriver
"""; 93 | 94 | String expected = """ 95 | |=== 96 | |Short Name |Driver 97 | 98 | |`htmlunit` |http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/htmlunit/HtmlUnitDriver.html[org.openqa.selenium.htmlunit.HtmlUnitDriver] 99 | |`firefox` |http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/firefox/FirefoxDriver.html[org.openqa.selenium.firefox.FirefoxDriver] 100 | |`ie` |http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/ie/InternetExplorerDriver.html[org.openqa.selenium.ie.InternetExplorerDriver] 101 | |`chrome` |http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/chrome/ChromeDriver.html[org.openqa.selenium.chrome.ChromeDriver] 102 | |=== 103 | """; 104 | 105 | String actual = TableToAsciiDoc.convert(html); 106 | 107 | assertEquals(expected, actual); 108 | 109 | } 110 | } -------------------------------------------------------------------------------- /src/test/resources/nl/jworks/markdown_to_asciidoc/markup.feature: -------------------------------------------------------------------------------- 1 | # language: en 2 | @markup 3 | Feature: Markup 4 | In order to group content 5 | As a writer 6 | I want to be able to apply markup to text 7 | 8 | Scenario: Don't apply formatting 9 | Given the Markdown source 10 | """ 11 | Normal text 12 | """ 13 | When it is converted to AsciiDoc 14 | Then the result should match the AsciiDoc source 15 | """ 16 | Normal text 17 | """ 18 | 19 | Scenario: Don't apply formatting for one line over multiple lines 20 | Given the Markdown source 21 | """ 22 | Normal text 23 | Normal text 24 | """ 25 | When it is converted to AsciiDoc 26 | Then the result should match the AsciiDoc source 27 | """ 28 | Normal text 29 | Normal text 30 | """ 31 | 32 | Scenario: Don't apply formatting for multiple lines 33 | Given the Markdown source 34 | """ 35 | Normal text 36 | 37 | Normal text 38 | """ 39 | When it is converted to AsciiDoc 40 | Then the result should match the AsciiDoc source 41 | """ 42 | Normal text 43 | 44 | Normal text 45 | """ 46 | 47 | Scenario: Text, lists, text 48 | Given the Markdown source 49 | """ 50 | The support provides: 51 | 52 | * Understanding of implicit browser methods (e.g. `to()`, `at()`) in test classes (e.g. `extends GebSpec`) 53 | * Understanding of content defined via the Content DSL (within `Page` and `Module` classes only) 54 | * Completion in `at {}` and `content {}` blocks 55 | 56 | This effectively enables more authoring support with less explicit type information. The Geb development team would like to thank the good folks at JetBrains for adding this explicit support for Geb to IDEA. 57 | """ 58 | When it is converted to AsciiDoc 59 | Then the result should match the AsciiDoc source 60 | """ 61 | The support provides: 62 | 63 | * Understanding of implicit browser methods (e.g. `to()`, `at()`) in test classes (e.g. `extends GebSpec`) 64 | * Understanding of content defined via the Content DSL (within `Page` and `Module` classes only) 65 | * Completion in `at {}` and `content {}` blocks 66 | 67 | This effectively enables more authoring support with less explicit type information. The Geb development team would like to thank the good folks at JetBrains for adding this explicit support for Geb to IDEA. 68 | """ 69 | 70 | # @fixme 71 | # Scenario: Escaped characters 72 | # Given the Markdown source 73 | # """ 74 | # \*this text is surrounded by literal asterisks\* 75 | # """ 76 | # When it is converted to AsciiDoc 77 | # Then the result should match the AsciiDoc source 78 | # """ 79 | # +++*this text is surrounded by literal asterisks*+++ 80 | # """ 81 | 82 | Scenario: Make text bold 83 | Given the Markdown source 84 | """ 85 | **Bold text** 86 | __Bold text__ 87 | """ 88 | When it is converted to AsciiDoc 89 | Then the result should match the AsciiDoc source 90 | """ 91 | *Bold text* 92 | *Bold text* 93 | """ 94 | 95 | Scenario: Make text italic 96 | Given the Markdown source 97 | """ 98 | *Italic text* 99 | _Italic text_ 100 | """ 101 | When it is converted to AsciiDoc 102 | Then the result should match the AsciiDoc source 103 | """ 104 | _Italic text_ 105 | _Italic text_ 106 | """ 107 | 108 | Scenario: Make text mono 109 | Given the Markdown source 110 | """ 111 | `Mono text` 112 | """ 113 | When it is converted to AsciiDoc 114 | Then the result should match the AsciiDoc source 115 | """ 116 | `Mono text` 117 | """ 118 | 119 | Scenario: Make text bold and italic 120 | Given the Markdown source 121 | """ 122 | This is ***bold and italic*** text 123 | """ 124 | When it is converted to AsciiDoc 125 | Then the result should match the AsciiDoc source 126 | """ 127 | This is *_bold and italic_* text 128 | """ 129 | 130 | Scenario: Blockquotes 131 | Given the Markdown source 132 | """ 133 | > Blockquotes are very handy in email to emulate reply text. 134 | > This line is part of the same quote. 135 | 136 | Quote break. 137 | 138 | > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. 139 | """ 140 | When it is converted to AsciiDoc 141 | Then the result should match the AsciiDoc source 142 | """ 143 | ____ 144 | 145 | Blockquotes are very handy in email to emulate reply text. 146 | This line is part of the same quote. 147 | 148 | ____ 149 | 150 | Quote break. 151 | 152 | ____ 153 | 154 | This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ *Markdown* into a blockquote. 155 | 156 | ____ 157 | """ 158 | 159 | @blockquotes 160 | Scenario: Nested Blockquotes 161 | Given the Markdown source 162 | """ 163 | > > What's new? 164 | > 165 | > I've got Markdown in my AsciiDoc! 166 | > 167 | > > Like what? 168 | > 169 | > * Blockquotes 170 | > * Headings 171 | > * Fenced code blocks 172 | > 173 | > > Is there more? 174 | > 175 | > Yep. AsciiDoc and Markdown share a lot of common syntax already. 176 | """ 177 | When it is converted to AsciiDoc 178 | Then the result should match the AsciiDoc source 179 | """ 180 | ____ 181 | 182 | ________ 183 | 184 | What's new? 185 | 186 | ________ 187 | 188 | I've got Markdown in my AsciiDoc! 189 | 190 | ________ 191 | 192 | Like what? 193 | 194 | ________ 195 | 196 | * Blockquotes 197 | * Headings 198 | * Fenced code blocks 199 | 200 | ________ 201 | 202 | Is there more? 203 | 204 | ________ 205 | 206 | Yep. AsciiDoc and Markdown share a lot of common syntax already. 207 | 208 | ____ 209 | """ 210 | 211 | Scenario: Superscript 212 | Given the Markdown source 213 | """ 214 | superscript^2^ 215 | """ 216 | When it is converted to AsciiDoc 217 | Then the result should match the AsciiDoc source 218 | """ 219 | superscript^2^ 220 | """ 221 | 222 | Scenario: Subscript 223 | Given the Markdown source 224 | """ 225 | CO~2~ 226 | """ 227 | When it is converted to AsciiDoc 228 | Then the result should match the AsciiDoc source 229 | """ 230 | CO~2~ 231 | """ 232 | 233 | Scenario: Double angle bracket quoting 234 | Given the Markdown source 235 | """ 236 | <> 237 | """ 238 | When it is converted to AsciiDoc 239 | Then the result should match the AsciiDoc source 240 | """ 241 | «hello» 242 | """ 243 | 244 | Scenario: Double quoting 245 | Given the Markdown source 246 | """ 247 | "hello" 248 | """ 249 | When it is converted to AsciiDoc 250 | Then the result should match the AsciiDoc source 251 | """ 252 | "hello" 253 | """ 254 | 255 | 256 | Scenario: Single quoting 257 | Given the Markdown source 258 | """ 259 | 'hello' 260 | """ 261 | When it is converted to AsciiDoc 262 | Then the result should match the AsciiDoc source 263 | """ 264 | 'hello' 265 | """ 266 | 267 | Scenario: Apostroph 268 | Given the Markdown source 269 | """ 270 | a'a 271 | """ 272 | When it is converted to AsciiDoc 273 | Then the result should match the AsciiDoc source 274 | """ 275 | a'a 276 | """ 277 | 278 | Scenario: Ellipsis two 279 | Given the Markdown source 280 | """ 281 | a...a 282 | a. . .a 283 | """ 284 | When it is converted to AsciiDoc 285 | Then the result should match the AsciiDoc source 286 | """ 287 | a…a 288 | a…a 289 | """ 290 | 291 | Scenario: Em Dash 292 | Given the Markdown source 293 | """ 294 | a---a 295 | """ 296 | When it is converted to AsciiDoc 297 | Then the result should match the AsciiDoc source 298 | """ 299 | a—a 300 | """ 301 | 302 | Scenario: En Dash 303 | Given the Markdown source 304 | """ 305 | a--a 306 | """ 307 | When it is converted to AsciiDoc 308 | Then the result should match the AsciiDoc source 309 | """ 310 | a–a 311 | """ 312 | 313 | Scenario: Nbsp 314 | Given the Markdown source 315 | """ 316 | << a a >> 317 | """ 318 | When it is converted to AsciiDoc 319 | Then the result should match the AsciiDoc source 320 | """ 321 | «{nbsp}a a{nbsp}» 322 | """ 323 | 324 | Scenario: Should recognize a hard line break 325 | Given the Markdown source 326 | """ 327 | Roses are red,{sp}{sp} 328 | Violets are blue.{sp} 329 | Sort of blue. 330 | More like violet. 331 | """ 332 | When it is converted to AsciiDoc 333 | Then the result should match the AsciiDoc source 334 | """ 335 | Roses are red, + 336 | Violets are blue. 337 | Sort of blue. 338 | More like violet. 339 | """ 340 | 341 | Scenario: Strikethrough 342 | Given the Markdown source 343 | """ 344 | This is ~~striked~~ text 345 | """ 346 | When it is converted to AsciiDoc 347 | Then the result should match the AsciiDoc source 348 | """ 349 | This is [line-through]#striked# text 350 | """ 351 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.markdown-asciidoc 8 | markdown-to-asciidoc 9 | 3.0.0-SNAPSHOT 10 | 11 | Markdown To AsciiDoc 12 | A small, lightweight converter for Markdown to AsciiDoc written in Java, and based on Pegdown. 13 | https://github.com/markdown-asciidoc/markdown-to-asciidoc 14 | 15 | 16 | 17 | The Apache License, Version 2.0 18 | http://www.apache.org/licenses/LICENSE-2.0.txt 19 | 20 | 21 | 22 | 23 | 24 | bodiam 25 | Erik Pragt 26 | erik.pragt@jworks.nl 27 | https://github.com/bodiam 28 | 29 | 30 | mojavelinux 31 | Dan Allen 32 | https://mojavelinux.com 33 | 34 | 35 | matozoid 36 | Danny van Bruggen 37 | http://www.laamella.com 38 | 39 | 40 | ahus1 41 | Alexander Schwartz 42 | alexander.schwartz@gmx.net 43 | https://www.ahus1.de 44 | 45 | 46 | dgautier 47 | David Gautier 48 | https://dgautier.github.io 49 | 50 | 51 | 52 | 53 | scm:git:https://github.com/markdown-asciidoc/markdown-to-asciidoc.git 54 | https://github.com/markdown-asciidoc/markdown-to-asciidoc.git 55 | scm:git:https://github.com/markdown-asciidoc/markdown-to-asciidoc.git 56 | HEAD 57 | 58 | 59 | 60 | UTF-8 61 | UTF-8 62 | 17 63 | 7.27.0 64 | ${java.version} 65 | ${java.version} 66 | UTF-8 67 | UTF-8 68 | 69 | 70 | 71 | 72 | org.pegdown 73 | pegdown 74 | 1.6.0 75 | 76 | 77 | org.parboiled 78 | parboiled-java 79 | 80 | 81 | 82 | 83 | org.parboiled 84 | parboiled-java 85 | 1.4.1 86 | 87 | 88 | org.jsoup 89 | jsoup 90 | 1.21.1 91 | 92 | 93 | commons-io 94 | commons-io 95 | 2.20.0 96 | test 97 | 98 | 99 | io.cucumber 100 | cucumber-picocontainer 101 | ${cucumber.version} 102 | test 103 | 104 | 105 | io.cucumber 106 | cucumber-junit 107 | ${cucumber.version} 108 | test 109 | 110 | 111 | io.cucumber 112 | cucumber-java 113 | ${cucumber.version} 114 | test 115 | 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-surefire-plugin 123 | 3.5.3 124 | 125 | --add-opens java.base/java.lang=ALL-UNNAMED 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | ossrh 134 | 135 | 136 | ossrh 137 | Central Repository OSSRH 138 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 139 | 140 | 141 | ossrh 142 | Central Repository OSSRH 143 | https://s01.oss.sonatype.org/content/repositories/snapshots 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-gpg-plugin 151 | 3.2.8 152 | 153 | 154 | sign-artifacts 155 | verify 156 | 157 | sign 158 | 159 | 160 | 161 | 162 | 164 | 165 | --pinentry-mode 166 | loopback 167 | 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-source-plugin 173 | 3.3.1 174 | 175 | 176 | attach-sources 177 | 178 | jar-no-fork 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-javadoc-plugin 186 | 3.11.2 187 | 188 | 189 | attach-javadocs 190 | 191 | jar 192 | 193 | 194 | 195 | 196 | 197 | org.sonatype.plugins 198 | nexus-staging-maven-plugin 199 | 1.7.0 200 | true 201 | 202 | ossrh 203 | https://s01.oss.sonatype.org/ 204 | true 205 | 206 | 207 | 208 | org.apache.maven.plugins 209 | maven-release-plugin 210 | 3.1.1 211 | 212 | [ci skip] 213 | 214 | 215 | 216 | 217 | 218 | 219 | github 220 | 221 | 222 | github 223 | GitHub Packages 224 | https://maven.pkg.github.com/markdown-asciidoc/markdown-to-asciidoc 225 | 226 | 227 | 228 | 229 | coverage 230 | 231 | 232 | 233 | org.jacoco 234 | jacoco-maven-plugin 235 | 0.8.13 236 | 237 | 238 | prepare-agent 239 | 240 | prepare-agent 241 | 242 | 243 | 244 | jacoco-report 245 | verify 246 | 247 | report 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /src/test/resources/testsuite.md: -------------------------------------------------------------------------------- 1 | ``` 2 | summary(cars$dist) 3 | summary(cars$speed) 4 | ``` 5 | 6 | # 2-paragraphs-hard-return-spaces 7 | 8 | This is a first paragraph, 9 | on multiple lines. 10 | 11 | This is a second paragraph. 12 | There are spaces in between the two. 13 | 14 | # 2-paragraphs-hard-return 15 | 16 | This is a first paragraph, 17 | on multiple lines. 18 | 19 | This is a second paragraph 20 | which has multiple lines too. 21 | 22 | # 2-paragraphs-line-returns 23 | 24 | A first paragraph. 25 | 26 | 27 | 28 | A second paragraph after 3 CR (carriage return). 29 | 30 | # 2-paragraphs-line-spaces 31 | 32 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 33 | 34 | A few spaces and a new long long long long long long long long long long long long long long long long paragraph on 1 line. 35 | 36 | # 2-paragraphs-line-tab 37 | 38 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 39 | 40 | 1 tab to separate them and a new long long long long long long long long long long long long long long long long paragraph on 1 line. 41 | 42 | # 2-paragraphs-line 43 | 44 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 45 | 46 | A new long long long long long long long long long long long long long long long long paragraph on 1 line. 47 | 48 | # ampersand-text-flow 49 | 50 | An ampersand & in the text flow is escaped as an html entity. 51 | 52 | # ampersand-uri 53 | 54 | There is an [ampersand](http://validator.w3.org/check?uri=http://www.w3.org/&verbose=1) in the URI. 55 | 56 | # asterisk-near-text 57 | 58 | This is \*an asterisk which should stay as is. 59 | 60 | # asterisk 61 | 62 | This is * an asterisk which should stay as is. 63 | 64 | # backslash-escape 65 | 66 | \\ backslash 67 | \` backtick 68 | \* asterisk 69 | \_ underscore 70 | \{\} curly braces 71 | \[\] square brackets 72 | \(\) parentheses 73 | \# hash mark 74 | \+ plus sign 75 | \- minus sign (hyphen) 76 | \. dot 77 | \! exclamation mark 78 | 79 | # blockquote-added-markup 80 | 81 | > # heading level 1 82 | > 83 | > paragraph 84 | 85 | # blockquote-line-2-paragraphs 86 | 87 | >A blockquote with a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long line. 88 | 89 | >and a second very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long line. 90 | 91 | # blockquote-line 92 | 93 | >This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph in a blockquote. 94 | 95 | # blockquote-multiline-1-space-begin 96 | 97 | > A blockquote 98 | > on multiple lines 99 | > like this. 100 | 101 | # blockquote-multiline-1-space-end 102 | 103 | >A blockquote 104 | >on multiple lines 105 | >like this. 106 | 107 | # blockquote-multiline-2-paragraphs 108 | 109 | >A blockquote 110 | >on multiple lines 111 | >like this. 112 | > 113 | >But it has 114 | >two paragraphs. 115 | 116 | # blockquote-multiline 117 | 118 | >A blockquote 119 | >on multiple lines 120 | >like this 121 | 122 | # blockquote-nested-multiplereturn-level1 123 | 124 | > This is the first level of quoting. 125 | > 126 | > > This is nested blockquote. 127 | > 128 | > Back to the first level. 129 | 130 | 131 | # blockquote-nested-multiplereturn 132 | 133 | > This is the first level of quoting. 134 | > 135 | > > This is nested blockquote. 136 | 137 | # blockquote-nested-return-level1 138 | 139 | > This is the first level of quoting. 140 | > > This is nested blockquote. 141 | > Back to the first level. 142 | 143 | 144 | # blockquote-nested 145 | 146 | > This is the first level of quoting. 147 | > > This is nested blockquote. 148 | 149 | 150 | # code-1-tab 151 | 152 | 10 PRINT HELLO INFINITE 153 | 20 GOTO 10 154 | 155 | # code-4-spaces-escaping 156 | 157 | 10 PRINT < > & 158 | 20 GOTO 10 159 | 160 | # code-4-spaces 161 | 162 | 10 PRINT HELLO INFINITE 163 | 20 GOTO 10 164 | 165 | # em-middle-word 166 | 167 | as*te*risks 168 | 169 | # em-star 170 | 171 | *single asterisks* 172 | 173 | # em-underscore 174 | 175 | _single underscores_ 176 | 177 | # entities-text-flow 178 | 179 | HTML entities are written using ampersand notation: © 180 | 181 | # header-level1-equal-underlined 182 | 183 | This is an H1 184 | ============= 185 | 186 | # header-level1-hash-sign-closed 187 | 188 | # This is an H1 # 189 | 190 | # header-level1-hash-sign-trailing-1-space 191 | 192 | # This is an H1 193 | 194 | # header-level1-hash-sign-trailing-2-spaces 195 | 196 | # this is an h1 with two trailing spaces 197 | A new paragraph. 198 | 199 | # header-level1-hash-sign 200 | 201 | # This is an H1 202 | 203 | # header-level2-dash-underlined 204 | 205 | This is an H2 206 | ------------- 207 | 208 | # header-level2-hash-sign-closed 209 | 210 | ## This is an H2 ## 211 | 212 | # header-level2-hash-sign 213 | 214 | ## This is an H2 215 | 216 | # header-level3-hash-sign-closed 217 | 218 | ### This is an H3 ### 219 | 220 | # header-level3-hash-sign 221 | 222 | ### This is an H3 223 | 224 | # header-level4-hash-sign-closed 225 | 226 | #### This is an H4 #### 227 | 228 | # header-level4-hash-sign 229 | 230 | #### This is an H4 231 | 232 | # header-level5-hash-sign-closed 233 | 234 | ##### This is an H5 ##### 235 | 236 | # header-level5-hash-sign 237 | 238 | ##### This is an H5 239 | 240 | # header-level6-hash-sign-closed 241 | 242 | ###### This is an H6 ###### 243 | 244 | # header-level6-hash-sign 245 | 246 | ###### This is an H6 247 | 248 | # horizontal-rule-3-dashes-spaces 249 | 250 | - - - 251 | 252 | # horizontal-rule-3-dashes 253 | 254 | --- 255 | 256 | # horizontal-rule-3-stars 257 | 258 | *** 259 | 260 | # horizontal-rule-3-underscores 261 | 262 | ___ 263 | 264 | # horizontal-rule-7-dashes 265 | 266 | ------- 267 | 268 | # img-idref-title 269 | 270 | ![HTML5][h5] 271 | 272 | [h5]: http://www.w3.org/html/logo/img/mark-word-icon.png "HTML5 for everyone" 273 | 274 | # img-idref 275 | 276 | ![HTML5][h5] 277 | 278 | [h5]: http://www.w3.org/html/logo/img/mark-word-icon.png 279 | 280 | # img-title 281 | 282 | ![HTML5](http://www.w3.org/html/logo/img/mark-word-icon.png "HTML5 logo for everyone") 283 | 284 | # img 285 | 286 | ![HTML5](http://www.w3.org/html/logo/img/mark-word-icon.png) 287 | 288 | # inline-code-escaping-entities 289 | 290 | We love ` and &` for everything 291 | 292 | # inline-code-with-visible-backtick 293 | 294 | ``We love `code` for everything`` 295 | 296 | # inline-code 297 | 298 | ``We love `code` for everything`` 299 | 300 | # line-break-2-spaces 301 | 302 | A first sentence 303 | and a line break. 304 | 305 | # line-break-5-spaces 306 | 307 | A first sentence 308 | and a line break. 309 | 310 | # link-automatic 311 | 312 | This is an automatic link 313 | 314 | # link-bracket-paranthesis-title 315 | 316 | [W3C](http://www.w3.org/ "Discover w3c") 317 | 318 | # link-bracket-paranthesis 319 | 320 | [W3C](http://www.w3.org/) 321 | 322 | # link-idref-angle-bracket 323 | 324 | [World Wide Web Consortium][w3c] 325 | 326 | [w3c]: 327 | 328 | # link-idref-implicit-spaces 329 | 330 | [World Wide Web Consortium][] 331 | 332 | [World Wide Web Consortium]: http://www.w3.org/ 333 | 334 | # link-idref-implicit 335 | 336 | [w3c][] 337 | 338 | [w3c]: http://www.w3.org/ 339 | 340 | # link-idref-space 341 | 342 | [World Wide Web Consortium] [w3c] 343 | 344 | [w3c]: http://www.w3.org/ 345 | 346 | # link-idref-title-next-line 347 | 348 | [World Wide Web Consortium][w3c] 349 | 350 | [w3c]: http://www.w3.org/ 351 | "Discover W3C" 352 | 353 | # link-idref-title-paranthesis 354 | 355 | [World Wide Web Consortium][w3c] 356 | 357 | [w3c]: http://www.w3.org/ (Discover w3c) 358 | 359 | # link-idref-title-single-quote 360 | 361 | [World Wide Web Consortium][w3c] 362 | 363 | [w3c]: http://www.w3.org/ 'Discover w3c' 364 | 365 | # link-idref-title 366 | 367 | [World Wide Web Consortium][w3c] 368 | 369 | [w3c]: http://www.w3.org/ "Discover w3c" 370 | 371 | # link-idref 372 | 373 | [World Wide Web Consortium][w3c] 374 | 375 | [w3c]: http://www.w3.org/ 376 | 377 | # list-blockquote 378 | 379 | * a list containing a blockquote 380 | 381 | > this the blockquote in the list 382 | 383 | # list-code-1-space 384 | 385 | * a 386 | 387 | b 388 | 389 | 390 | # list-code 391 | 392 | * a list containing a block of code 393 | 394 | 10 PRINT HELLO INFINITE 395 | 20 GOTO 10 396 | 397 | # list-multiparagraphs-tab 398 | 399 | * This is a list item with two paragraphs. Lorem ipsum dolor 400 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 401 | mi posuere lectus. 402 | 403 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 404 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 405 | sit amet velit. 406 | 407 | * Suspendisse id sem consectetuer libero luctus adipiscing. 408 | 409 | # list-multiparagraphs 410 | 411 | * This is a list item with two paragraphs. Lorem ipsum dolor 412 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 413 | mi posuere lectus. 414 | 415 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 416 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 417 | sit amet velit. 418 | 419 | * Suspendisse id sem consectetuer libero luctus adipiscing. 420 | 421 | # ordered-list-escaped 422 | 423 | 1\. ordered list escape 424 | 425 | # ordered-list-inner-par-list 426 | 427 | 1. 1 428 | 429 | - inner par list 430 | 431 | 2. 2 432 | 433 | 434 | # ordered-list-items-random-number 435 | 436 | 1. list item 1 437 | 8. list item 2 438 | 1. list item 3 439 | 440 | # ordered-list-items 441 | 442 | 1. list item 1 443 | 2. list item 2 444 | 3. list item 3 445 | 446 | # paragraph-hard-return 447 | 448 | This is a paragraph 449 | on multiple lines 450 | with hard return. 451 | 452 | # paragraph-line 453 | 454 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 455 | 456 | # paragraph-trailing-leading-spaces 457 | 458 | This is a paragraph with a trailing and leading space. 459 | 460 | # paragraph-trailing-tab 461 | 462 | This is a paragraph with 1 trailing tab. 463 | 464 | # paragraphs-2-leading-spaces 465 | 466 | This is a paragraph with 2 leading spaces. 467 | 468 | # paragraphs-3-leading-spaces 469 | 470 | This is a paragraph with 3 leading spaces. 471 | 472 | # paragraphs-leading-space 473 | 474 | This is a paragraph with 1 leading space. 475 | 476 | # paragraphs-trailing-spaces 477 | 478 | This is a paragraph with a trailing space. 479 | 480 | # strong-middle-word 481 | 482 | as**te**risks 483 | 484 | # strong-star 485 | 486 | **double asterisks** 487 | 488 | # strong-underscore 489 | 490 | __double underscores__ 491 | 492 | # unordered-list-items-asterisk 493 | 494 | * list item 1 495 | * list item 2 496 | * list item 3 497 | 498 | 499 | # unordered-list-items-dashsign 500 | 501 | - list item 1 502 | - list item 2 503 | - list item 3 504 | 505 | # unordered-list-items-leading-1space 506 | 507 | * list item 1 508 | * list item 2 509 | * list item 3 510 | 511 | # unordered-list-items-leading-2spaces 512 | 513 | * list item 1 514 | * list item 2 515 | * list item 3 516 | 517 | # unordered-list-items-leading-3spaces 518 | 519 | * list item 1 520 | * list item 2 521 | * list item 3 522 | 523 | # unordered-list-items-plussign 524 | 525 | + list item 1 526 | + list item 2 527 | + list item 3 528 | 529 | # unordered-list-paragraphs 530 | 531 | * list item in paragraph 532 | 533 | * another list item in paragraph 534 | 535 | # unordered-list-unindented-content 536 | 537 | * This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph in a list. 538 | * and yet another long long long long long long long long long long long long long long long long long long long long long long line. 539 | 540 | # unordered-list-with-indented-content 541 | 542 | * This is a list item 543 | with the content on 544 | multiline and indented. 545 | * And this another list item 546 | with the same principle. 547 | 548 | # fenced-code-block 549 | 550 | ``` 551 | a 552 | ``` 553 | 554 | # link-idref-implicit-no-bracket 555 | 556 | [a] 557 | 558 | [a]: b 559 | 560 | 561 | # utf8 562 | 563 | € 564 | 565 | # autolink-no-bracket 566 | 567 | http://a.com 568 | 569 | 570 | # fenced-code-block 571 | 572 | ``` 573 | a 574 | ``` 575 | 576 | # link-idref-implicit-no-bracket 577 | 578 | [a] 579 | 580 | [a]: b 581 | 582 | 583 | # utf8 584 | 585 | € 586 | 587 | # link-idref-implicit-no-bracket 588 | 589 | [a] 590 | 591 | [a]: b 592 | 593 | 594 | # utf8 595 | 596 | € 597 | 598 | # fenced-code-block 599 | 600 | ``` 601 | a 602 | ``` 603 | 604 | # link-idref-implicit-no-bracket 605 | 606 | [a] 607 | 608 | [a]: b 609 | 610 | 611 | # utf8 612 | 613 | € 614 | 615 | # fenced-code-block 616 | 617 | ``` 618 | a 619 | ``` 620 | 621 | # link-idref-implicit-no-bracket 622 | 623 | [a] 624 | 625 | [a]: b 626 | 627 | 628 | # utf8 629 | 630 | € 631 | 632 | # fenced-code-block 633 | 634 | ``` 635 | a 636 | ``` 637 | 638 | # utf8 639 | 640 | € 641 | 642 | # autolink-no-bracket 643 | 644 | http://a.com 645 | 646 | 647 | # fenced-code-block 648 | 649 | ``` 650 | a 651 | ``` 652 | 653 | # link-idref-implicit-no-bracket 654 | 655 | [a] 656 | 657 | [a]: b 658 | 659 | 660 | # utf8 661 | 662 | € 663 | 664 | # fenced-code-block 665 | 666 | ``` 667 | a 668 | ``` 669 | 670 | # link-idref-implicit-no-bracket 671 | 672 | [a] 673 | 674 | [a]: b 675 | 676 | 677 | # utf8 678 | 679 | € 680 | 681 | # fenced-code-block 682 | 683 | ``` 684 | a 685 | ``` 686 | 687 | # link-idref-implicit-no-bracket 688 | 689 | [a] 690 | 691 | [a]: b 692 | 693 | 694 | # utf8 695 | 696 | € 697 | 698 | # fenced-code-block 699 | 700 | ``` 701 | a 702 | ``` 703 | 704 | # link-idref-implicit-no-bracket 705 | 706 | [a] 707 | 708 | [a]: b 709 | 710 | 711 | # utf8 712 | 713 | € 714 | 715 | # fenced-code-block 716 | 717 | ``` 718 | a 719 | ``` 720 | 721 | # link-idref-implicit-no-bracket 722 | 723 | [a] 724 | 725 | [a]: b 726 | 727 | 728 | # utf8 729 | 730 | € 731 | 732 | # fenced-code-block 733 | 734 | ``` 735 | a 736 | ``` 737 | 738 | # link-idref-implicit-no-bracket 739 | 740 | [a] 741 | 742 | [a]: b 743 | 744 | 745 | # utf8 746 | 747 | € 748 | 749 | # fenced-code-block 750 | 751 | ``` 752 | a 753 | ``` 754 | 755 | # link-idref-implicit-no-bracket 756 | 757 | [a] 758 | 759 | [a]: b 760 | 761 | 762 | # utf8 763 | 764 | € 765 | 766 | # fenced-code-block 767 | 768 | ``` 769 | a 770 | ``` 771 | 772 | # link-idref-implicit-no-bracket 773 | 774 | [a] 775 | 776 | [a]: b 777 | 778 | 779 | # utf8 780 | 781 | € 782 | 783 | # fenced-code-block 784 | 785 | ``` 786 | a 787 | ``` 788 | 789 | # link-idref-implicit-no-bracket 790 | 791 | [a] 792 | 793 | [a]: b 794 | 795 | 796 | # utf8 797 | 798 | € 799 | 800 | -------------------------------------------------------------------------------- /src/test/resources/testsuite.adoc: -------------------------------------------------------------------------------- 1 | [source] 2 | ---- 3 | summary(cars$dist) 4 | summary(cars$speed) 5 | ---- 6 | 7 | 8 | = 2-paragraphs-hard-return-spaces 9 | 10 | This is a first paragraph, 11 | on multiple lines. 12 | This is a second paragraph. 13 | There are spaces in between the two. 14 | 15 | = 2-paragraphs-hard-return 16 | 17 | This is a first paragraph, 18 | on multiple lines. 19 | This is a second paragraph 20 | which has multiple lines too. 21 | 22 | = 2-paragraphs-line-returns 23 | 24 | A first paragraph. 25 | A second paragraph after 3 CR (carriage return). 26 | 27 | = 2-paragraphs-line-spaces 28 | 29 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 30 | A few spaces and a new long long long long long long long long long long long long long long long long paragraph on 1 line. 31 | 32 | = 2-paragraphs-line-tab 33 | 34 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 35 | 1 tab to separate them and a new long long long long long long long long long long long long long long long long paragraph on 1 line. 36 | 37 | = 2-paragraphs-line 38 | 39 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 40 | A new long long long long long long long long long long long long long long long long paragraph on 1 line. 41 | 42 | = ampersand-text-flow 43 | 44 | An ampersand & in the text flow is escaped as an html entity. 45 | 46 | = ampersand-uri 47 | 48 | There is an http://validator.w3.org/check?uri=http://www.w3.org/&verbose=1[ampersand] in the URI. 49 | 50 | = asterisk-near-text 51 | 52 | This is *an asterisk which should stay as is. 53 | 54 | = asterisk 55 | 56 | This is * an asterisk which should stay as is. 57 | 58 | = backslash-escape 59 | 60 | \ backslash 61 | ` backtick 62 | * asterisk 63 | _ underscore 64 | {} curly braces 65 | [] square brackets 66 | () parentheses 67 | # hash mark 68 | + plus sign 69 | - minus sign (hyphen) 70 | . dot 71 | ! exclamation mark 72 | 73 | = blockquote-added-markup 74 | 75 | ____ 76 | 77 | = heading level 1 78 | 79 | paragraph 80 | ____ 81 | 82 | 83 | = blockquote-line-2-paragraphs 84 | 85 | ____ 86 | A blockquote with a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long line. 87 | and a second very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long line. 88 | ____ 89 | 90 | 91 | = blockquote-line 92 | 93 | ____ 94 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph in a blockquote. 95 | ____ 96 | 97 | 98 | = blockquote-multiline-1-space-begin 99 | 100 | ____ 101 | A blockquote 102 | on multiple lines 103 | like this. 104 | ____ 105 | 106 | 107 | = blockquote-multiline-1-space-end 108 | 109 | ____ 110 | A blockquote 111 | on multiple lines 112 | like this. 113 | ____ 114 | 115 | 116 | = blockquote-multiline-2-paragraphs 117 | 118 | ____ 119 | A blockquote 120 | on multiple lines 121 | like this. 122 | But it has 123 | two paragraphs. 124 | ____ 125 | 126 | 127 | = blockquote-multiline 128 | 129 | ____ 130 | A blockquote 131 | on multiple lines 132 | like this 133 | ____ 134 | 135 | 136 | = blockquote-nested-multiplereturn-level1 137 | 138 | ____ 139 | This is the first level of quoting. 140 | ________ 141 | This is nested blockquote. 142 | ________ 143 | 144 | Back to the first level. 145 | ____ 146 | 147 | 148 | = blockquote-nested-multiplereturn 149 | 150 | ____ 151 | This is the first level of quoting. 152 | ________ 153 | This is nested blockquote. 154 | ________ 155 | 156 | ____ 157 | 158 | 159 | = blockquote-nested-return-level1 160 | 161 | ____ 162 | This is the first level of quoting. 163 | ________ 164 | This is nested blockquote. 165 | Back to the first level. 166 | ________ 167 | 168 | ____ 169 | 170 | 171 | = blockquote-nested 172 | 173 | ____ 174 | This is the first level of quoting. 175 | ________ 176 | This is nested blockquote. 177 | ________ 178 | 179 | ____ 180 | 181 | 182 | = code-1-tab 183 | 184 | [source] 185 | ---- 186 | 10 PRINT HELLO INFINITE 187 | 20 GOTO 10 188 | ---- 189 | 190 | 191 | = code-4-spaces-escaping 192 | 193 | [source] 194 | ---- 195 | 10 PRINT < > & 196 | 20 GOTO 10 197 | ---- 198 | 199 | 200 | = code-4-spaces 201 | 202 | [source] 203 | ---- 204 | 10 PRINT HELLO INFINITE 205 | 20 GOTO 10 206 | ---- 207 | 208 | 209 | = em-middle-word 210 | 211 | as*te*risks 212 | 213 | = em-star 214 | 215 | _single asterisks_ 216 | 217 | = em-underscore 218 | 219 | _single underscores_ 220 | 221 | = entities-text-flow 222 | 223 | HTML entities are written using ampersand notation: © 224 | 225 | = header-level1-equal-underlined 226 | 227 | 228 | = This is an H1 229 | 230 | 231 | = header-level1-hash-sign-closed 232 | 233 | 234 | = This is an H1 235 | 236 | 237 | = header-level1-hash-sign-trailing-1-space 238 | 239 | # This is an H1 240 | 241 | = header-level1-hash-sign-trailing-2-spaces 242 | 243 | 244 | = this is an h1 with two trailing spaces 245 | 246 | A new paragraph. 247 | 248 | = header-level1-hash-sign 249 | 250 | 251 | = This is an H1 252 | 253 | 254 | = header-level2-dash-underlined 255 | 256 | 257 | == This is an H2 258 | 259 | 260 | = header-level2-hash-sign-closed 261 | 262 | 263 | == This is an H2 264 | 265 | 266 | = header-level2-hash-sign 267 | 268 | 269 | == This is an H2 270 | 271 | 272 | = header-level3-hash-sign-closed 273 | 274 | 275 | === This is an H3 276 | 277 | 278 | = header-level3-hash-sign 279 | 280 | 281 | === This is an H3 282 | 283 | 284 | = header-level4-hash-sign-closed 285 | 286 | 287 | ==== This is an H4 288 | 289 | 290 | = header-level4-hash-sign 291 | 292 | 293 | ==== This is an H4 294 | 295 | 296 | = header-level5-hash-sign-closed 297 | 298 | 299 | ===== This is an H5 300 | 301 | 302 | = header-level5-hash-sign 303 | 304 | 305 | ===== This is an H5 306 | 307 | 308 | = header-level6-hash-sign-closed 309 | 310 | 311 | ====== This is an H6 312 | 313 | 314 | = header-level6-hash-sign 315 | 316 | 317 | ====== This is an H6 318 | 319 | 320 | = horizontal-rule-3-dashes-spaces 321 | 322 | ''' 323 | 324 | = horizontal-rule-3-dashes 325 | 326 | ''' 327 | 328 | = horizontal-rule-3-stars 329 | 330 | ''' 331 | 332 | = horizontal-rule-3-underscores 333 | 334 | ''' 335 | 336 | = horizontal-rule-7-dashes 337 | 338 | ''' 339 | 340 | = img-idref-title 341 | 342 | image:http://www.w3.org/html/logo/img/mark-word-icon.png[HTML5] 343 | 344 | = img-idref 345 | 346 | image:http://www.w3.org/html/logo/img/mark-word-icon.png[HTML5] 347 | 348 | = img-title 349 | 350 | image:http://www.w3.org/html/logo/img/mark-word-icon.png[HTML5] 351 | 352 | = img 353 | 354 | image:http://www.w3.org/html/logo/img/mark-word-icon.png[HTML5] 355 | 356 | = inline-code-escaping-entities 357 | 358 | We love `<code> and &` for everything 359 | 360 | = inline-code-with-visible-backtick 361 | 362 | `We love `code` for everything` 363 | 364 | = inline-code 365 | 366 | `We love `code` for everything` 367 | 368 | = line-break-2-spaces 369 | 370 | A first sentence 371 | and a line break. 372 | 373 | = line-break-5-spaces 374 | 375 | A first sentence 376 | and a line break. 377 | 378 | = link-automatic 379 | 380 | This is an automatic link http://www.w3.org/[http://www.w3.org/] 381 | 382 | = link-bracket-paranthesis-title 383 | 384 | http://www.w3.org/[W3C] 385 | 386 | = link-bracket-paranthesis 387 | 388 | http://www.w3.org/[W3C] 389 | 390 | = link-idref-angle-bracket 391 | 392 | http://www.w3.org/[World Wide Web Consortium] 393 | 394 | = link-idref-implicit-spaces 395 | 396 | http://www.w3.org/[World Wide Web Consortium] 397 | 398 | = link-idref-implicit 399 | 400 | http://www.w3.org/[w3c] 401 | 402 | = link-idref-space 403 | 404 | http://www.w3.org/[World Wide Web Consortium] 405 | 406 | = link-idref-title-next-line 407 | 408 | http://www.w3.org/[World Wide Web Consortium] 409 | "Discover W3C" 410 | 411 | = link-idref-title-paranthesis 412 | 413 | http://www.w3.org/[World Wide Web Consortium] 414 | 415 | = link-idref-title-single-quote 416 | 417 | http://www.w3.org/[World Wide Web Consortium] 418 | 419 | = link-idref-title 420 | 421 | http://www.w3.org/[World Wide Web Consortium] 422 | 423 | = link-idref 424 | 425 | http://www.w3.org/[World Wide Web Consortium] 426 | 427 | = list-blockquote 428 | 429 | * 430 | a list containing a blockquote 431 | ____ 432 | this the blockquote in the list 433 | ____ 434 | 435 | 436 | = list-code-1-space 437 | 438 | * 439 | a 440 | [source] 441 | ---- 442 | b 443 | ---- 444 | 445 | 446 | = list-code 447 | 448 | * 449 | a list containing a block of code 450 | [source] 451 | ---- 452 | 10 PRINT HELLO INFINITE 453 | 20 GOTO 10 454 | ---- 455 | 456 | 457 | = list-multiparagraphs-tab 458 | 459 | * 460 | This is a list item with two paragraphs. Lorem ipsum dolor 461 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 462 | mi posuere lectus. 463 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 464 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 465 | sit amet velit. 466 | * 467 | Suspendisse id sem consectetuer libero luctus adipiscing. 468 | 469 | = list-multiparagraphs 470 | 471 | * 472 | This is a list item with two paragraphs. Lorem ipsum dolor 473 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 474 | mi posuere lectus. 475 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 476 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 477 | sit amet velit. 478 | * 479 | Suspendisse id sem consectetuer libero luctus adipiscing. 480 | 481 | = ordered-list-escaped 482 | 483 | 1. ordered list escape 484 | 485 | = ordered-list-inner-par-list 486 | 487 | 488 | 1. 489 | 1 490 | 1. inner par list 491 | 1. 492 | 2 493 | 494 | = ordered-list-items-random-number 495 | 496 | 497 | 1. list item 1 498 | 1. list item 2 499 | 1. list item 3 500 | 501 | = ordered-list-items 502 | 503 | 504 | 1. list item 1 505 | 1. list item 2 506 | 1. list item 3 507 | 508 | = paragraph-hard-return 509 | 510 | This is a paragraph 511 | on multiple lines 512 | with hard return. 513 | 514 | = paragraph-line 515 | 516 | This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph on 1 line. 517 | 518 | = paragraph-trailing-leading-spaces 519 | 520 | This is a paragraph with a trailing and leading space. 521 | 522 | = paragraph-trailing-tab 523 | 524 | This is a paragraph with 1 trailing tab. 525 | 526 | = paragraphs-2-leading-spaces 527 | 528 | This is a paragraph with 2 leading spaces. 529 | 530 | = paragraphs-3-leading-spaces 531 | 532 | This is a paragraph with 3 leading spaces. 533 | 534 | = paragraphs-leading-space 535 | 536 | This is a paragraph with 1 leading space. 537 | 538 | = paragraphs-trailing-spaces 539 | 540 | This is a paragraph with a trailing space. 541 | 542 | = strong-middle-word 543 | 544 | as**te**risks 545 | 546 | = strong-star 547 | 548 | *double asterisks* 549 | 550 | = strong-underscore 551 | 552 | *double underscores* 553 | 554 | = unordered-list-items-asterisk 555 | 556 | * list item 1 557 | * list item 2 558 | * list item 3 559 | 560 | = unordered-list-items-dashsign 561 | 562 | * list item 1 563 | * list item 2 564 | * list item 3 565 | 566 | = unordered-list-items-leading-1space 567 | 568 | * list item 1 569 | * list item 2 570 | * list item 3 571 | 572 | = unordered-list-items-leading-2spaces 573 | 574 | * list item 1 575 | * list item 2 576 | * list item 3 577 | 578 | = unordered-list-items-leading-3spaces 579 | 580 | * list item 1 581 | * list item 2 582 | * list item 3 583 | 584 | = unordered-list-items-plussign 585 | 586 | * list item 1 587 | * list item 2 588 | * list item 3 589 | 590 | = unordered-list-paragraphs 591 | 592 | * 593 | list item in paragraph 594 | * 595 | another list item in paragraph 596 | 597 | = unordered-list-unindented-content 598 | 599 | * This a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long paragraph in a list. 600 | * and yet another long long long long long long long long long long long long long long long long long long long long long long line. 601 | 602 | = unordered-list-with-indented-content 603 | 604 | * This is a list item 605 | with the content on 606 | multiline and indented. 607 | * And this another list item 608 | with the same principle. 609 | 610 | = fenced-code-block 611 | 612 | [source] 613 | ---- 614 | a 615 | ---- 616 | 617 | 618 | = link-idref-implicit-no-bracket 619 | 620 | link:b[a] 621 | 622 | = utf8 623 | 624 | € 625 | 626 | = autolink-no-bracket 627 | 628 | http://a.com[http://a.com] 629 | 630 | = fenced-code-block 631 | 632 | [source] 633 | ---- 634 | a 635 | ---- 636 | 637 | 638 | = link-idref-implicit-no-bracket 639 | 640 | link:b[a] 641 | 642 | = utf8 643 | 644 | € 645 | 646 | = link-idref-implicit-no-bracket 647 | 648 | link:b[a] 649 | 650 | = utf8 651 | 652 | € 653 | 654 | = fenced-code-block 655 | 656 | [source] 657 | ---- 658 | a 659 | ---- 660 | 661 | 662 | = link-idref-implicit-no-bracket 663 | 664 | link:b[a] 665 | 666 | = utf8 667 | 668 | € 669 | 670 | = fenced-code-block 671 | 672 | [source] 673 | ---- 674 | a 675 | ---- 676 | 677 | 678 | = link-idref-implicit-no-bracket 679 | 680 | link:b[a] 681 | 682 | = utf8 683 | 684 | € 685 | 686 | = fenced-code-block 687 | 688 | [source] 689 | ---- 690 | a 691 | ---- 692 | 693 | 694 | = utf8 695 | 696 | € 697 | 698 | = autolink-no-bracket 699 | 700 | http://a.com[http://a.com] 701 | 702 | = fenced-code-block 703 | 704 | [source] 705 | ---- 706 | a 707 | ---- 708 | 709 | 710 | = link-idref-implicit-no-bracket 711 | 712 | link:b[a] 713 | 714 | = utf8 715 | 716 | € 717 | 718 | = fenced-code-block 719 | 720 | [source] 721 | ---- 722 | a 723 | ---- 724 | 725 | 726 | = link-idref-implicit-no-bracket 727 | 728 | link:b[a] 729 | 730 | = utf8 731 | 732 | € 733 | 734 | = fenced-code-block 735 | 736 | [source] 737 | ---- 738 | a 739 | ---- 740 | 741 | 742 | = link-idref-implicit-no-bracket 743 | 744 | link:b[a] 745 | 746 | = utf8 747 | 748 | € 749 | 750 | = fenced-code-block 751 | 752 | [source] 753 | ---- 754 | a 755 | ---- 756 | 757 | 758 | = link-idref-implicit-no-bracket 759 | 760 | link:b[a] 761 | 762 | = utf8 763 | 764 | € 765 | 766 | = fenced-code-block 767 | 768 | [source] 769 | ---- 770 | a 771 | ---- 772 | 773 | 774 | = link-idref-implicit-no-bracket 775 | 776 | link:b[a] 777 | 778 | = utf8 779 | 780 | € 781 | 782 | = fenced-code-block 783 | 784 | [source] 785 | ---- 786 | a 787 | ---- 788 | 789 | 790 | = link-idref-implicit-no-bracket 791 | 792 | link:b[a] 793 | 794 | = utf8 795 | 796 | € 797 | 798 | = fenced-code-block 799 | 800 | [source] 801 | ---- 802 | a 803 | ---- 804 | 805 | 806 | = link-idref-implicit-no-bracket 807 | 808 | link:b[a] 809 | 810 | = utf8 811 | 812 | € 813 | 814 | = fenced-code-block 815 | 816 | [source] 817 | ---- 818 | a 819 | ---- 820 | 821 | 822 | = link-idref-implicit-no-bracket 823 | 824 | link:b[a] 825 | 826 | = utf8 827 | 828 | € 829 | 830 | = fenced-code-block 831 | 832 | [source] 833 | ---- 834 | a 835 | ---- 836 | 837 | 838 | = link-idref-implicit-no-bracket 839 | 840 | link:b[a] 841 | 842 | = utf8 843 | 844 | € -------------------------------------------------------------------------------- /src/main/java/nl/jworks/markdown_to_asciidoc/ToAsciiDocSerializer.java: -------------------------------------------------------------------------------- 1 | package nl.jworks.markdown_to_asciidoc; 2 | 3 | import nl.jworks.markdown_to_asciidoc.code.Linguist; 4 | import nl.jworks.markdown_to_asciidoc.html.TableToAsciiDoc; 5 | import org.parboiled.common.StringUtils; 6 | import org.pegdown.LinkRenderer; 7 | import org.pegdown.Printer; 8 | import org.pegdown.ast.*; 9 | 10 | import java.util.*; 11 | 12 | import static org.parboiled.common.Preconditions.checkArgNotNull; 13 | 14 | public class ToAsciiDocSerializer implements Visitor { 15 | public static final String HARD_LINE_BREAK_MARKDOWN = " \n"; 16 | protected String source; 17 | protected Printer printer; 18 | protected final Map references = new HashMap<>(); 19 | protected final Map abbreviations = new HashMap<>(); 20 | protected final LinkRenderer linkRenderer = new LinkRenderer(); 21 | 22 | protected TableNode currentTableNode; 23 | protected int currentTableColumn; 24 | protected boolean inTableHeader; 25 | 26 | protected char listMarker; 27 | protected int listLevel = 0; 28 | protected int blockQuoteLevel = 0; 29 | 30 | // Experimental feature. 31 | protected boolean autoDetectLanguageType; 32 | protected Linguist linguist; 33 | 34 | protected RootNode rootNode; 35 | 36 | @SuppressWarnings("unused") 37 | public ToAsciiDocSerializer(RootNode rootNode) { 38 | this(rootNode, null); 39 | } 40 | 41 | public ToAsciiDocSerializer(RootNode rootNode, String source) { 42 | this.printer = new Printer(); 43 | this.linguist = new Linguist(); 44 | this.autoDetectLanguageType = false; 45 | checkArgNotNull(rootNode, "rootNode"); 46 | this.rootNode = rootNode; 47 | this.source = source; 48 | } 49 | 50 | public String toAsciiDoc() { 51 | cleanAst(rootNode); 52 | rootNode.accept(this); 53 | String result = normalizeWhitelines(printer.getString()); 54 | printer.clear(); 55 | return result; 56 | } 57 | 58 | public void visit(RootNode node) { 59 | for (ReferenceNode refNode : node.getReferences()) { 60 | visitChildren(refNode); 61 | references.put(normalize(printer.getString()), refNode); 62 | printer.clear(); 63 | } 64 | for (AbbreviationNode abbrNode : node.getAbbreviations()) { 65 | visitChildren(abbrNode); 66 | String abbr = printer.getString(); 67 | printer.clear(); 68 | abbrNode.getExpansion().accept(this); 69 | String expansion = printer.getString(); 70 | abbreviations.put(abbr, expansion); 71 | printer.clear(); 72 | } 73 | visitChildren(node); 74 | } 75 | 76 | public void visit(AbbreviationNode node) { 77 | } 78 | 79 | @Override 80 | public void visit(AnchorLinkNode node) { 81 | printer.print(node.getText()); 82 | } 83 | 84 | public void visit(AutoLinkNode node) { 85 | printLink(linkRenderer.render(node)); 86 | } 87 | 88 | public void visit(BlockQuoteNode node) { 89 | printer.println().println(); 90 | 91 | blockQuoteLevel += 4; 92 | 93 | repeat('_', blockQuoteLevel); 94 | printer.println(); 95 | visitChildren(node); 96 | printer.println().println(); 97 | repeat('_', blockQuoteLevel); 98 | 99 | blockQuoteLevel -= 4; 100 | 101 | printer.println(); 102 | } 103 | 104 | public void visit(BulletListNode node) { 105 | char prevListMarker = listMarker; 106 | listMarker = '*'; 107 | 108 | listLevel = listLevel + 1; 109 | visitChildren(node); 110 | listLevel = listLevel - 1; 111 | 112 | listMarker = prevListMarker; 113 | } 114 | 115 | public void visit(CodeNode node) { 116 | printer.print('`'); 117 | printer.print(node.getText()); 118 | printer.print('`'); 119 | } 120 | 121 | public void visit(DefinitionListNode node) { 122 | printer.println(); 123 | visitChildren(node); 124 | } 125 | 126 | public void visit(DefinitionTermNode node) { 127 | visitChildren(node); 128 | printer.indent(2); 129 | printer.print("::").println(); 130 | } 131 | 132 | public void visit(DefinitionNode node) { 133 | visitChildren(node); 134 | if (printer.indent > 0) { 135 | printer.indent(-2); 136 | } 137 | printer.println(); 138 | } 139 | 140 | public void visit(ExpImageNode node) { 141 | String text = printChildrenToString(node); 142 | LinkRenderer.Rendering imageRenderer = linkRenderer.render(node, text); 143 | Node linkNode; 144 | if ((linkNode = findParentNode(node, rootNode)) instanceof ExpLinkNode) { 145 | printImageTagWithLink(imageRenderer, linkRenderer.render((ExpLinkNode) linkNode, null)); 146 | } else { 147 | printImageTag(linkRenderer.render(node, text)); 148 | } 149 | } 150 | 151 | public void visit(ExpLinkNode node) { 152 | String text = printChildrenToString(node); 153 | if (text.startsWith("image:")) { 154 | printer.print(text); 155 | } else { 156 | printLink(linkRenderer.render(node, text)); 157 | } 158 | } 159 | 160 | public void visit(HeaderNode node) { 161 | printer.println().println(); 162 | repeat('=', node.getLevel()); 163 | printer.print(' '); 164 | visitChildren(node); 165 | printer.println().println(); 166 | } 167 | 168 | private void repeat(char c, int times) { 169 | for (int i = 0; i < times; i++) { 170 | printer.print(c); 171 | } 172 | } 173 | 174 | public void visit(HtmlBlockNode node) { 175 | String text = node.getText(); 176 | if (!text.isEmpty()) printer.println(); 177 | 178 | if (text.startsWith(""); 333 | visitChildren(node); 334 | printer.print(""); 335 | } 336 | 337 | public void visit(TableCellNode node) { 338 | // String tag = inTableHeader ? "th" : "td"; 339 | List columns = currentTableNode.getColumns(); 340 | TableColumnNode column = columns.get(Math.min(currentTableColumn, columns.size() - 1)); 341 | 342 | String pstr = printer.getString(); 343 | if (!pstr.isEmpty()) { 344 | if (pstr.endsWith("\n") || pstr.endsWith(" ")) { 345 | printer.print("|"); 346 | } else { 347 | printer.print(" |"); 348 | } 349 | } else { 350 | printer.print("|"); 351 | } 352 | column.accept(this); 353 | if (node.getColSpan() > 1) printer.print(" colspan=\"").print(Integer.toString(node.getColSpan())).print('"'); 354 | visitChildren(node); 355 | 356 | currentTableColumn += node.getColSpan(); 357 | } 358 | 359 | public void visit(TableColumnNode node) { 360 | // nothing here yet 361 | } 362 | 363 | public void visit(TableHeaderNode node) { 364 | inTableHeader = true; 365 | // printIndentedTag(node, "thead"); 366 | 367 | visitChildren(node); 368 | 369 | inTableHeader = false; 370 | } 371 | 372 | private boolean ifColumnsHaveAlignmentSpecified(List columns) { 373 | for (TableColumnNode column : columns) { 374 | if (column.getAlignment() != TableColumnNode.Alignment.None) { 375 | return true; 376 | } 377 | } 378 | return false; 379 | } 380 | 381 | private String getColumnAlignment(List columns) { 382 | 383 | List result = new ArrayList<>(); 384 | 385 | for (TableColumnNode column : columns) { 386 | switch (column.getAlignment()) { 387 | case None: 388 | case Left: 389 | result.add("<"); 390 | break; 391 | case Right: 392 | result.add(">"); 393 | break; 394 | case Center: 395 | result.add("^"); 396 | break; 397 | default: 398 | throw new IllegalStateException(); 399 | } 400 | } 401 | 402 | return String.join(",", result); 403 | } 404 | 405 | 406 | public void visit(TableNode node) { 407 | currentTableNode = node; 408 | 409 | List columns = node.getColumns(); 410 | 411 | if (ifColumnsHaveAlignmentSpecified(columns)) { 412 | printer.println(); 413 | printer.print("[cols=\""); 414 | printer.print(getColumnAlignment(columns)); 415 | printer.print("\"]"); 416 | printer.println(); 417 | } else { 418 | printer.println(); 419 | } 420 | 421 | printer.print("|==="); 422 | visitChildren(node); 423 | printer.println(); 424 | printer.print("|==="); 425 | printer.println(); 426 | 427 | currentTableNode = null; 428 | } 429 | 430 | public void visit(TableRowNode node) { 431 | currentTableColumn = 0; 432 | 433 | printer.println(); 434 | 435 | visitChildren(node); 436 | // printIndentedTag(node, "tr"); 437 | 438 | if (inTableHeader) { 439 | printer.println(); 440 | } 441 | } 442 | 443 | public void visit(VerbatimNode node) { 444 | printer.println(); 445 | 446 | String type = node.getType(); 447 | String text = node.getText(); 448 | 449 | if (autoDetectLanguageType) { 450 | type = linguist.detectLanguage(text); 451 | } 452 | 453 | if (!type.isEmpty()) { 454 | printer.print("[source," + type + "]"); 455 | } 456 | 457 | printer.println(); 458 | repeat('-', 4); 459 | printer.println(); 460 | printer.print(text); 461 | repeat('-', 4); 462 | printer.println().println(); 463 | } 464 | 465 | public void visit(WikiLinkNode node) { 466 | printLink(linkRenderer.render(node)); 467 | } 468 | 469 | public void visit(TextNode node) { 470 | if (abbreviations.isEmpty()) { 471 | printer.print(node.getText()); 472 | } else { 473 | printWithAbbreviations(node.getText()); 474 | } 475 | } 476 | 477 | public void visit(SpecialTextNode node) { 478 | printer.printEncoded(node.getText()); 479 | } 480 | 481 | public void visit(SuperNode node) { 482 | visitChildren(node); 483 | } 484 | 485 | public void visit(Node node) { 486 | throw new RuntimeException("Don't know how to handle node " + node); 487 | } 488 | 489 | // helpers 490 | 491 | protected void visitChildren(AbstractNode node) { 492 | for (Node child : node.getChildren()) { 493 | child.accept(this); 494 | } 495 | } 496 | 497 | 498 | /** 499 | * Removes superfluous nodes from the tree. 500 | * 501 | * @param node The node to clean. 502 | */ 503 | protected void cleanAst(Node node) { 504 | List children = node.getChildren(); 505 | for (int i = 0, len = children.size(); i < len; i++) { 506 | Node c = children.get(i); 507 | if (c instanceof RootNode && c.getChildren().size() == 1) { 508 | children.set(i, c.getChildren().get(0)); 509 | } else if (c.getClass().equals(SuperNode.class) && c.getChildren().size() == 1) { 510 | children.set(i, c.getChildren().get(0)); 511 | } 512 | 513 | cleanAst(c); 514 | } 515 | } 516 | 517 | protected void printNodeSurroundedBy(AbstractNode node, String token) { 518 | printer.print(token); 519 | visitChildren(node); 520 | printer.print(token); 521 | } 522 | 523 | protected void printImageTag(LinkRenderer.Rendering rendering) { 524 | printer.print("image:"); 525 | printer.print(rendering.href); 526 | printer.print('['); 527 | printTextWithQuotesIfNeeded(printer, rendering.text); 528 | printer.print(']'); 529 | } 530 | 531 | protected void printImageTagWithLink(LinkRenderer.Rendering image, LinkRenderer.Rendering link) { 532 | printer.print("image:").print(image.href).print('['); 533 | if (image.text != null && !image.text.isEmpty()) { 534 | printTextWithQuotesIfNeeded(printer, image.text); 535 | printer.print(','); 536 | } 537 | 538 | printer.print("link=").print(link.href).print(']'); 539 | } 540 | 541 | protected void printLink(LinkRenderer.Rendering rendering) { 542 | String uri = rendering.href; 543 | String text = rendering.text; 544 | 545 | if (uri.startsWith("#")) { 546 | printer.print("<<").print(uri.substring(1)).print(',').print(text).print(">>"); 547 | } else { 548 | if (!uri.contains("://")) { 549 | uri = "link:" + uri; 550 | } 551 | printer.print(uri); 552 | if (!uri.equals(text)) { 553 | printer.print('['); 554 | printTextWithQuotesIfNeeded(printer, rendering.text); 555 | printer.print(']'); 556 | } 557 | } 558 | } 559 | 560 | protected String printChildrenToString(SuperNode node) { 561 | Printer priorPrinter = printer; 562 | printer = new Printer(); 563 | visitChildren(node); 564 | String result = printer.getString(); 565 | printer = priorPrinter; 566 | return result; 567 | } 568 | 569 | protected String normalize(String string) { 570 | StringBuilder sb = new StringBuilder(); 571 | for (int i = 0; i < string.length(); i++) { 572 | char c = string.charAt(i); 573 | switch (c) { 574 | case ' ': 575 | case '\n': 576 | case '\t': 577 | continue; 578 | } 579 | sb.append(Character.toLowerCase(c)); 580 | } 581 | return sb.toString(); 582 | } 583 | 584 | protected String normalizeWhitelines(String text) { 585 | // replace all double or more empty lines with single empty lines 586 | return text.replaceAll("(?m)^[ \t]*\r?\n{2,}", "\n").trim(); 587 | } 588 | 589 | protected void printTextWithQuotesIfNeeded(Printer p, String text) { 590 | if (text != null && !text.isEmpty()) { 591 | if (text.contains(",")) { 592 | p.print('"').print(text).print('"'); 593 | } else { 594 | p.print(text); 595 | } 596 | } 597 | } 598 | 599 | protected void printWithAbbreviations(String string) { 600 | Map> expansions = null; 601 | 602 | for (Map.Entry entry : abbreviations.entrySet()) { 603 | // first check, whether we have a legal match 604 | String abbr = entry.getKey(); 605 | 606 | int ix = 0; 607 | while (true) { 608 | int sx = string.indexOf(abbr, ix); 609 | if (sx == -1) break; 610 | 611 | // only allow whole word matches 612 | ix = sx + abbr.length(); 613 | 614 | if (sx > 0 && Character.isLetterOrDigit(string.charAt(sx - 1))) continue; 615 | if (ix < string.length() && Character.isLetterOrDigit(string.charAt(ix))) { 616 | continue; 617 | } 618 | 619 | // ok, legal match so save an expansions "task" for all matches 620 | if (expansions == null) { 621 | expansions = new TreeMap<>(); 622 | } 623 | expansions.put(sx, entry); 624 | } 625 | } 626 | 627 | if (expansions != null) { 628 | int ix = 0; 629 | for (Map.Entry> entry : expansions.entrySet()) { 630 | int sx = entry.getKey(); 631 | String abbr = entry.getValue().getKey(); 632 | String expansion = entry.getValue().getValue(); 633 | 634 | printer.printEncoded(string.substring(ix, sx)); 635 | printer.print("'); 642 | printer.printEncoded(abbr); 643 | printer.print(""); 644 | ix = sx + abbr.length(); 645 | } 646 | printer.print(string.substring(ix)); 647 | } else { 648 | printer.print(string); 649 | } 650 | } 651 | 652 | protected Node findParentNode(Node target, Node from) { 653 | if (target.equals(rootNode)) { 654 | return null; 655 | } 656 | 657 | Node candidate; 658 | 659 | for (Node c : from.getChildren()) { 660 | if (target.equals(c)) { 661 | return from; 662 | } else if ((candidate = findParentNode(target, c)) != null) { 663 | return candidate; 664 | } 665 | } 666 | 667 | return null; 668 | } 669 | 670 | protected boolean isFirstChild(Node parent, Node child) { 671 | return child.equals(parent.getChildren().get(0)); 672 | } 673 | 674 | protected boolean isListItemText(Node node) { 675 | if (listLevel == 0) { 676 | return false; 677 | } else { 678 | Node parent = findParentNode(node, rootNode); 679 | return (parent instanceof ListItemNode && isFirstChild(parent, node)); 680 | } 681 | } 682 | } 683 | --------------------------------------------------------------------------------