summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Seelmann2013-05-19 11:30:28 (EDT)
committerGerrit Code Review @ Eclipse.org2013-06-02 17:40:36 (EDT)
commitaea430d058cda2ad9d99ba4b05e91cb9b9c2f5a0 (patch)
treeaef3d8e71b853792b7b9a1284ea438bcd05cd285
parentc8a9a32029921f67b20f67aead26f2bee6f95ba9 (diff)
downloadorg.eclipse.mylyn.docs-aea430d058cda2ad9d99ba4b05e91cb9b9c2f5a0.zip
org.eclipse.mylyn.docs-aea430d058cda2ad9d99ba4b05e91cb9b9c2f5a0.tar.gz
org.eclipse.mylyn.docs-aea430d058cda2ad9d99ba4b05e91cb9b9c2f5a0.tar.bz2
404095: [Markdown] Add support for links and imagesrefs/changes/21/12521/4
* Added support for links (inline style, reference style, automatic links). * Added support for images (inline syle, reference style). * Added validation rules for local references and reference style links/images. Also changed processing of nested blocks within quote blocks. Prior the line offset was not considered which became evident when implementing validation rules for reference style links. Change-Id: I943bdbc9ecf03c1c3f7f6f3e83de46db83bbff15 Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=404095
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/plugin.xml11
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinition.java59
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionParser.java57
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionUsageTracker.java103
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownContentState.java53
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/CodeBlock.java15
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HeadingBlock.java22
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HorizontalRuleBlock.java20
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/InlineHtmlBlock.java50
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/LinkDefinitionBlock.java38
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/NestableBlock.java32
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ParagraphBlock.java10
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/QuoteBlock.java71
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/UnderlinedHeadingBlock.java18
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/AutomaticLinkReplacementToken.java49
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineImageReplacementToken.java53
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineLinkReplacementToken.java54
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleImageReplacementToken.java66
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleLinkReplacementToken.java67
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/util/ReadAheadDispatcher.java42
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/LinkDefinitionValidationRule.java86
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/MarkdownReferenceValidationRule.java25
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/Messages.java32
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/messages.properties12
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java22
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionParserTest.java147
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionValidationRuleTest.java132
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java300
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageMiscellaneousTest.java112
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageSpanElementsTest.java252
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java17
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTestBase.java9
-rw-r--r--org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownReferenceValidationRuleTest.java58
34 files changed, 1752 insertions, 346 deletions
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/META-INF/MANIFEST.MF b/org.eclipse.mylyn.wikitext.markdown.core/META-INF/MANIFEST.MF
index 28e69cd..94a90ae 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.wikitext.markdown.core/META-INF/MANIFEST.MF
@@ -7,9 +7,11 @@ Bundle-Vendor: %Bundle-Vendor.0
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: org.eclipse.mylyn.wikitext.core;bundle-version="1.5.0",
org.eclipse.team.core;resolution:=optional
-Export-Package: org.eclipse.mylyn.internal.wikitext.markdown.core.block;x-internal:=true,
+Export-Package: org.eclipse.mylyn.internal.wikitext.markdown.core;x-internal:=true,
+ org.eclipse.mylyn.internal.wikitext.markdown.core.block;x-internal:=true,
org.eclipse.mylyn.internal.wikitext.markdown.core.phrase;x-internal:=true,
org.eclipse.mylyn.internal.wikitext.markdown.core.token;x-internal:=true,
org.eclipse.mylyn.internal.wikitext.markdown.core.util;x-internal:=true,
+ org.eclipse.mylyn.internal.wikitext.markdown.core.validation;x-internal:=true,
org.eclipse.mylyn.wikitext.markdown.core
Bundle-Localization: plugin
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/plugin.xml b/org.eclipse.mylyn.wikitext.markdown.core/plugin.xml
index 303771a..9cb9c35 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/plugin.xml
+++ b/org.eclipse.mylyn.wikitext.markdown.core/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<!--
- Copyright (c) 2012 Stefan Seelmann and others.
+ Copyright (c) 2012, 2013 Stefan Seelmann and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
@@ -26,4 +26,13 @@
<extension point="org.eclipse.team.core.fileTypes">
<fileTypes extension="markdown" type="text"/>
</extension>
+
+ <extension point="org.eclipse.mylyn.wikitext.core.markupValidationRule">
+ <rule markupLanguage="Markdown"
+ class="org.eclipse.mylyn.internal.wikitext.markdown.core.validation.MarkdownReferenceValidationRule">
+ </rule>
+ <rule markupLanguage="Markdown"
+ class="org.eclipse.mylyn.internal.wikitext.markdown.core.validation.LinkDefinitionValidationRule">
+ </rule>
+ </extension>
</plugin>
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinition.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinition.java
new file mode 100644
index 0000000..f11aeeb
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinition.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core;
+
+/**
+ * Bean that holds information about reference-style link definitions.
+ *
+ * @author Stefan Seelmann
+ */
+public class LinkDefinition {
+
+ private final String id;
+
+ private final String url;
+
+ private final String title;
+
+ private final int offset;
+
+ private final int length;
+
+ public LinkDefinition(String id, String url, String title, int offset, int length) {
+ this.id = id;
+ this.url = url;
+ this.title = title;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionParser.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionParser.java
new file mode 100644
index 0000000..679de4d
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionParser.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Parser for link definitions in Markdown.
+ *
+ * @author Stefan Seelmann
+ */
+public class LinkDefinitionParser {
+
+ private static final String ID_REGEX = " {0,3}(\\[([^]]+?)\\]\\:)"; //$NON-NLS-1$
+
+ private static final String URL_REGEX = "\\s+(?=[<]?(([^>\\s]+)))(?:<\\3>|\\3)"; //$NON-NLS-1$
+
+ private static final String TITLE_REGEX = "(?:\\s+[\"'\\(](.+)[\"'\\)])?"; //$NON-NLS-1$
+
+ public static final Pattern LINK_DEFINITION_PATTERN = Pattern.compile(ID_REGEX + URL_REGEX + TITLE_REGEX);
+
+ private Map<String, LinkDefinition> linkDefinitions;
+
+ public void parse(String markupContent) {
+ linkDefinitions = new HashMap<String, LinkDefinition>();
+ Matcher matcher = LINK_DEFINITION_PATTERN.matcher(markupContent);
+ while (matcher.find()) {
+ String id = matcher.group(2);
+ String url = matcher.group(3);
+ String title = matcher.group(5);
+ int offset = matcher.start(1);
+ int length = matcher.end() - offset;
+ linkDefinitions.put(id.toLowerCase(), new LinkDefinition(id, url, title, offset, length));
+ }
+ }
+
+ public LinkDefinition getLinkDefinition(String id) {
+ return linkDefinitions.get(id.toLowerCase());
+ }
+
+ public Map<String, LinkDefinition> getLinkDefinitions() {
+ return linkDefinitions;
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionUsageTracker.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionUsageTracker.java
new file mode 100644
index 0000000..d100925
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/LinkDefinitionUsageTracker.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.mylyn.wikitext.core.parser.Locator;
+
+/**
+ * This class tracks which link definitions are used, unused, and missing.
+ *
+ * @author Stefan Seelmann
+ */
+public class LinkDefinitionUsageTracker {
+
+ private final Locator locator;
+
+ private final LinkDefinitionParser linkDefinitionParser;
+
+ private final Set<String> usedLinkDefinitionIds;
+
+ private final List<Position> missingLinkDefinitionPositions;
+
+ public LinkDefinitionUsageTracker(Locator locator, LinkDefinitionParser linkDefinitionParser) {
+ this.locator = locator;
+ this.linkDefinitionParser = linkDefinitionParser;
+ usedLinkDefinitionIds = new HashSet<String>();
+ missingLinkDefinitionPositions = new ArrayList<LinkDefinitionUsageTracker.Position>();
+ }
+
+ public void linkDefinitionRequested(String id) {
+ // ignore
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition(id);
+ if (linkDefinition == null) {
+ int offset = locator.getDocumentOffset();
+ int length = locator.getLineSegmentEndOffset() - locator.getLineCharacterOffset();
+ Position position = new Position(id, offset, length);
+ missingLinkDefinitionPositions.add(position);
+ } else {
+ usedLinkDefinitionIds.add(id.toLowerCase());
+ }
+ }
+
+ public List<Position> getUnusedLinkDefinitionPositions() {
+ List<Position> usedLinkDefinitionPositions = new ArrayList<Position>();
+ Map<String, LinkDefinition> linkDefinitions = linkDefinitionParser.getLinkDefinitions();
+ for (Entry<String, LinkDefinition> entry : linkDefinitions.entrySet()) {
+ String id = entry.getKey();
+ if (!usedLinkDefinitionIds.contains(id.toLowerCase())) {
+ LinkDefinition linkDefinition = entry.getValue();
+ Position position = new Position(linkDefinition.getId(), linkDefinition.getOffset(),
+ linkDefinition.getLength());
+ usedLinkDefinitionPositions.add(position);
+ }
+ }
+ return usedLinkDefinitionPositions;
+ }
+
+ public List<Position> getMissingLinkDefinitionPositions() {
+ return missingLinkDefinitionPositions;
+ }
+
+ public class Position {
+ private final String id;
+
+ int offset;
+
+ int length;
+
+ public Position(String id, int offset, int length) {
+ this.id = id;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownContentState.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownContentState.java
new file mode 100644
index 0000000..dd86d47
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownContentState.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core;
+
+import org.eclipse.mylyn.wikitext.core.parser.markup.ContentState;
+
+/**
+ * Extended version of ContentState that preprocesses Markdown for reference-style link support.
+ *
+ * @author Stefan Seelmann
+ */
+public class MarkdownContentState extends ContentState {
+
+ private LinkDefinitionParser linkDefinitionParser;
+
+ private LinkDefinitionUsageTracker linkDefinitionUsageTracker;
+
+ @Override
+ protected void setMarkupContent(String markupContent) {
+ super.setMarkupContent(markupContent);
+
+ linkDefinitionParser = new LinkDefinitionParser();
+ linkDefinitionParser.parse(markupContent);
+
+ linkDefinitionUsageTracker = new LinkDefinitionUsageTracker(this, linkDefinitionParser);
+ }
+
+ /**
+ * Gets the {@link LinkDefinition} for the given link identifier, or <code>null</code> if there is no such
+ * {@link LinkDefinition}.
+ *
+ * @param id
+ * the link identifier.
+ * @return the {@link LinkDefinition} or <code>null</code>
+ */
+ public LinkDefinition getLinkDefinition(String id) {
+ linkDefinitionUsageTracker.linkDefinitionRequested(id);
+ return linkDefinitionParser.getLinkDefinition(id);
+ }
+
+ public LinkDefinitionUsageTracker getLinkDefinitionUsageTracker() {
+ return linkDefinitionUsageTracker;
+ }
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/CodeBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/CodeBlock.java
index ec9343b..7a2d207 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/CodeBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/CodeBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -16,14 +16,13 @@ import java.util.regex.Pattern;
import org.eclipse.mylyn.wikitext.core.parser.Attributes;
import org.eclipse.mylyn.wikitext.core.parser.DocumentBuilder.BlockType;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
/**
* Markdown code block.
*
* @author Stefan Seelmann
*/
-public class CodeBlock extends Block {
+public class CodeBlock extends NestableBlock {
private static final Pattern startPattern = Pattern.compile("(?: {4}|\\t)((?: {4}|\\t)*)(.*)"); //$NON-NLS-1$
@@ -31,11 +30,7 @@ public class CodeBlock extends Block {
@Override
public boolean canStart(String line, int lineOffset) {
- if (lineOffset == 0) {
- return startPattern.matcher(line).matches();
- } else {
- return false;
- }
+ return startPattern.matcher(line.substring(lineOffset)).matches();
}
@Override
@@ -48,10 +43,10 @@ public class CodeBlock extends Block {
}
// extract the content
- Matcher matcher = startPattern.matcher(line);
+ Matcher matcher = startPattern.matcher(line.substring(offset));
if (!matcher.matches()) {
setClosed(true);
- return 0;
+ return offset;
}
String intent = matcher.group(1);
String content = matcher.group(2);
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HeadingBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HeadingBlock.java
index dfb423b..782a428 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HeadingBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HeadingBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -15,14 +15,13 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.mylyn.wikitext.core.parser.Attributes;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
/**
* Markdown headings.
*
* @author Stefan Seelmann
*/
-public class HeadingBlock extends Block {
+public class HeadingBlock extends NestableBlock {
private static final Pattern pattern = Pattern.compile("(#{1,6})\\s*(.+?)\\s*(?:#*\\s*)?"); //$NON-NLS-1$
@@ -30,22 +29,19 @@ public class HeadingBlock extends Block {
@Override
public boolean canStart(String line, int lineOffset) {
- if (lineOffset == 0) {
- matcher = pattern.matcher(line);
- return matcher.matches();
- } else {
- matcher = null;
- return false;
- }
+ matcher = pattern.matcher(line.substring(lineOffset));
+ return matcher.matches();
}
@Override
- public int processLineContent(String line, int offset) {
+ protected int processLineContent(String line, int offset) {
int level = matcher.group(1).length();
- String text = matcher.group(2);
builder.beginHeading(level, new Attributes());
- builder.characters(text);
+ int textStart = offset + matcher.start(2);
+ int textEnd = offset + matcher.end(2);
+ String lineExcludingClosingHash = line.substring(0, textEnd);
+ markupLanguage.emitMarkupLine(getParser(), state, lineExcludingClosingHash, textStart);
builder.endHeading();
setClosed(true);
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HorizontalRuleBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HorizontalRuleBlock.java
index fa3577f..3fe71c6 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HorizontalRuleBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/HorizontalRuleBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,22 +10,17 @@
*******************************************************************************/
package org.eclipse.mylyn.internal.wikitext.markdown.core.block;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
-
/**
* @author Stefan Seelmann
*/
-public class HorizontalRuleBlock extends Block {
+public class HorizontalRuleBlock extends NestableBlock {
private static final Pattern pattern = Pattern.compile("(\\*\\s*){3,}|(-\\s*){3,}|(_\\s*){3,}"); //$NON-NLS-1$
- private Matcher matcher;
-
@Override
- public int processLineContent(String line, int offset) {
+ protected int processLineContent(String line, int offset) {
builder.charactersUnescaped("<hr/>"); //$NON-NLS-1$
setClosed(true);
return -1;
@@ -33,13 +28,6 @@ public class HorizontalRuleBlock extends Block {
@Override
public boolean canStart(String line, int lineOffset) {
- if (lineOffset == 0) {
- matcher = pattern.matcher(line);
- return matcher.matches();
- } else {
- matcher = null;
- return false;
- }
+ return pattern.matcher(line.substring(lineOffset)).matches();
}
-
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/InlineHtmlBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/InlineHtmlBlock.java
index a9d0122..9f001ed 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/InlineHtmlBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/InlineHtmlBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,32 +11,38 @@
package org.eclipse.mylyn.internal.wikitext.markdown.core.block;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
+import java.util.regex.Pattern;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.AutomaticLinkReplacementToken;
/**
* Markdown inline HTML.
*
* @author Stefan Seelmann
*/
-public class InlineHtmlBlock extends Block {
-
- @Override
- public boolean canStart(String line, int lineOffset) {
- return line.startsWith("<"); //$NON-NLS-1$
- }
-
- @Override
- protected int processLineContent(String line, int offset) {
- // empty line: start new block
- if (markupLanguage.isEmptyLine(line)) {
- setClosed(true);
- return 0;
- }
-
- builder.charactersUnescaped(line);
- builder.characters("\n"); //$NON-NLS-1$
-
- return -1;
- }
+public class InlineHtmlBlock extends NestableBlock {
+
+ private static final Pattern AUTOMATIC_LINK_PATTERN = Pattern.compile(AutomaticLinkReplacementToken.AUTOMATIC_LINK_REGEX);
+
+ @Override
+ public boolean canStart(String line, int lineOffset) {
+ return line.substring(lineOffset).trim().startsWith("<") && !AUTOMATIC_LINK_PATTERN.matcher(line).matches(); //$NON-NLS-1$
+ }
+
+ @Override
+ protected int processLineContent(String line, int offset) {
+ String text = line.substring(offset);
+
+ // empty line: start new block
+ if (markupLanguage.isEmptyLine(text)) {
+ setClosed(true);
+ return offset;
+ }
+
+ builder.charactersUnescaped(text);
+ builder.characters("\n"); //$NON-NLS-1$
+
+ return -1;
+ }
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/LinkDefinitionBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/LinkDefinitionBlock.java
new file mode 100644
index 0000000..61e5dff
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/LinkDefinitionBlock.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.block;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinitionParser;
+
+/**
+ * Markdown link/image definitions. Does not emit anything.
+ *
+ * @author Stefan Seelmann
+ */
+public class LinkDefinitionBlock extends NestableBlock {
+
+ @Override
+ public boolean canStart(String line, int lineOffset) {
+ return LinkDefinitionParser.LINK_DEFINITION_PATTERN.matcher(line.substring(lineOffset)).matches();
+ }
+
+ @Override
+ protected int processLineContent(String line, int offset) {
+ if (markupLanguage.isEmptyLine(line.substring(offset))) {
+ setClosed(true);
+ return offset;
+ }
+
+ return -1;
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/NestableBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/NestableBlock.java
new file mode 100644
index 0000000..cfd5dd6
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/NestableBlock.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.block;
+
+import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
+
+/**
+ * Block that can be nested within a {@link QuoteBlock}.
+ *
+ * @author Stefan Seelmann
+ */
+public abstract class NestableBlock extends Block {
+
+ @Override
+ public int processLine(String line, int offset) {
+ return processLineContent(line, offset);
+ }
+
+ @Override
+ public NestableBlock clone() {
+ return (NestableBlock) super.clone();
+ }
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ParagraphBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ParagraphBlock.java
index 7bb5db6..101483d 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ParagraphBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ParagraphBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -13,14 +13,13 @@ package org.eclipse.mylyn.internal.wikitext.markdown.core.block;
import org.eclipse.mylyn.wikitext.core.parser.Attributes;
import org.eclipse.mylyn.wikitext.core.parser.DocumentBuilder.BlockType;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
/**
* Markdown default paragraph.
*
* @author Stefan Seelmann
*/
-public class ParagraphBlock extends Block {
+public class ParagraphBlock extends NestableBlock {
private int blockLineCount = 0;
@@ -32,15 +31,16 @@ public class ParagraphBlock extends Block {
@Override
protected int processLineContent(String line, int offset) {
+
// start of block
if (blockLineCount == 0) {
builder.beginBlock(BlockType.PARAGRAPH, new Attributes());
}
// empty line: start new block
- if (markupLanguage.isEmptyLine(line)) {
+ if (markupLanguage.isEmptyLine(line.substring(offset))) {
setClosed(true);
- return 0;
+ return offset;
}
// next line, does not convert to line break
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/QuoteBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/QuoteBlock.java
index 3972da1..40f53a2 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/QuoteBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/QuoteBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -23,11 +23,11 @@ import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
*
* @author Stefan Seelmann
*/
-public class QuoteBlock extends Block {
+public class QuoteBlock extends NestableBlock {
- private static final Pattern startPattern = Pattern.compile(">\\s*(.*)"); //$NON-NLS-1$
+ private static final Pattern startPattern = Pattern.compile(">\\s?(.*)"); //$NON-NLS-1$
- private static final Pattern linePattern = Pattern.compile("(?:>\\s*)?(.*)"); //$NON-NLS-1$
+ private static final Pattern linePattern = Pattern.compile("(?:>\\s?)?(.*)"); //$NON-NLS-1$
private int blockLineCount = 0;
@@ -35,59 +35,74 @@ public class QuoteBlock extends Block {
@Override
public boolean canStart(String line, int lineOffset) {
- if (lineOffset == 0) {
- return startPattern.matcher(line).matches();
- } else {
- return false;
- }
+ return startPattern.matcher(line.substring(lineOffset)).matches();
}
@Override
protected int processLineContent(String line, int offset) {
+ String text = line.substring(offset);
// start of block
if (blockLineCount == 0) {
builder.beginBlock(BlockType.QUOTE, new Attributes());
}
// empty line: end of block
- if (markupLanguage.isEmptyLine(line)) {
+ if (markupLanguage.isEmptyLine(text)) {
setClosed(true);
- return 0;
+ return offset;
}
// extract the content
- Matcher matcher = linePattern.matcher(line);
+ Matcher matcher = linePattern.matcher(text);
if (!matcher.matches()) {
setClosed(true);
- return 0;
+ return offset;
}
- String content = matcher.group(1);
+ int contentStart = offset + matcher.start(1);
- // determine nested block, at least the paragraph block must match
- for (Block block : getMarkupLanguage().getBlocks()) {
- if (block.canStart(content, 0)) {
- if (nestedBlock != null && nestedBlock.getClass() != block.getClass()) {
- nestedBlock.setClosed(true);
+ if (nestedBlock != null) {
+ if (!(nestedBlock instanceof QuoteBlock) && this.canStart(line, contentStart)) {
+ nestedBlock.setClosed(true);
+ nestedBlock = null;
+ processNextBlock(line, contentStart);
+ } else {
+ int processed = nestedBlock.processLine(line, contentStart);
+ if (nestedBlock.isClosed()) {
nestedBlock = null;
+ if (processed >= contentStart && processed < line.length()) {
+ processNextBlock(line, contentStart);
+ }
}
- if (nestedBlock == null) {
- nestedBlock = block.clone();
- nestedBlock.setParser(getParser());
- nestedBlock.setState(getState());
- }
- break;
}
+ } else {
+ processNextBlock(line, contentStart);
}
+ blockLineCount++;
+ return -1;
+ }
+
+ private void processNextBlock(String line, int contentStart) {
+ // determine nested block, at least the paragraph block must match
+ for (Block block : getMarkupLanguage().getBlocks()) {
+ if (block.canStart(line, contentStart)) {
+ nestedBlock = clone(block);
+ break;
+ }
+ }
// delegate content processing to nested block
- nestedBlock.processLine(content, 0);
+ nestedBlock.processLine(line, contentStart);
if (nestedBlock.isClosed()) {
nestedBlock = null;
}
+ }
- blockLineCount++;
- return -1;
+ private Block clone(Block block) {
+ Block clonedBlock = block.clone();
+ clonedBlock.setParser(getParser());
+ clonedBlock.setState(getState());
+ return clonedBlock;
}
@Override
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/UnderlinedHeadingBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/UnderlinedHeadingBlock.java
index 4261de0..486c6e8 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/UnderlinedHeadingBlock.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/UnderlinedHeadingBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -16,14 +16,13 @@ import java.util.regex.Pattern;
import org.eclipse.mylyn.internal.wikitext.markdown.core.util.LookAheadReader;
import org.eclipse.mylyn.internal.wikitext.markdown.core.util.ReadAheadBlock;
import org.eclipse.mylyn.wikitext.core.parser.Attributes;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
/**
* Markdown underlined headings.
*
* @author Stefan Seelmann
*/
-public class UnderlinedHeadingBlock extends Block implements ReadAheadBlock {
+public class UnderlinedHeadingBlock extends NestableBlock implements ReadAheadBlock {
private static final Pattern h1pattern = Pattern.compile("=+\\s*"); //$NON-NLS-1$
@@ -37,12 +36,14 @@ public class UnderlinedHeadingBlock extends Block implements ReadAheadBlock {
blockLineCount = 0;
level = 0;
String nextLine = lookAheadReader.lookAhead();
- if (nextLine == null) {
+ if (nextLine == null || nextLine.length() < lineOffset) {
return false;
- } else if (h1pattern.matcher(nextLine).matches()) {
+ }
+ String text = nextLine.substring(lineOffset);
+ if (h1pattern.matcher(text).matches()) {
level = 1;
return true;
- } else if (h2pattern.matcher(nextLine).matches()) {
+ } else if (h2pattern.matcher(text).matches()) {
level = 2;
return true;
} else {
@@ -57,10 +58,11 @@ public class UnderlinedHeadingBlock extends Block implements ReadAheadBlock {
}
@Override
- public int processLineContent(String line, int offset) {
+ protected int processLineContent(String line, int offset) {
+
if (blockLineCount == 0) {
builder.beginHeading(level, new Attributes());
- builder.characters(line);
+ markupLanguage.emitMarkupLine(getParser(), state, line, offset);
} else {
builder.endHeading();
setClosed(true);
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/AutomaticLinkReplacementToken.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/AutomaticLinkReplacementToken.java
new file mode 100644
index 0000000..07e6087
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/AutomaticLinkReplacementToken.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.token;
+
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElement;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElementProcessor;
+
+/**
+ * Detects automatic links: &lt;http://www.example.com&gt;.
+ *
+ * @author Stefan Seelmann
+ */
+public class AutomaticLinkReplacementToken extends PatternBasedElement {
+
+ public static final String AUTOMATIC_LINK_REGEX = "<((?:https?|ftp):[^'\">\\s]+)>"; //$NON-NLS-1$
+
+ @Override
+ protected String getPattern(int groupOffset) {
+ return AUTOMATIC_LINK_REGEX;
+ }
+
+ @Override
+ protected int getPatternGroupCount() {
+ return 1;
+ }
+
+ @Override
+ protected PatternBasedElementProcessor newProcessor() {
+ return new AutomaticLinkReplacementTokenProcessor();
+ }
+
+ private static class AutomaticLinkReplacementTokenProcessor extends PatternBasedElementProcessor {
+ @Override
+ public void emit() {
+ String href = group(1);
+ builder.link(href, href);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineImageReplacementToken.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineImageReplacementToken.java
new file mode 100644
index 0000000..384f3a0
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineImageReplacementToken.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.token;
+
+import org.eclipse.mylyn.wikitext.core.parser.ImageAttributes;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElement;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElementProcessor;
+
+/**
+ * Detects inline images: ![Alt text](/path/to/img.jpg "Optional title").
+ *
+ * @author Stefan Seelmann
+ */
+public class InlineImageReplacementToken extends PatternBasedElement {
+
+ @Override
+ protected String getPattern(int groupOffset) {
+ return "!\\[\\s*(.+?)\\s*\\]\\(\\s*(.+?)(?:\\s\"(.+?)\")?\\s*\\)"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected int getPatternGroupCount() {
+ return 3;
+ }
+
+ @Override
+ protected PatternBasedElementProcessor newProcessor() {
+ return new InlineLinkReplacementTokenProcessor();
+ }
+
+ private static class InlineLinkReplacementTokenProcessor extends PatternBasedElementProcessor {
+ @Override
+ public void emit() {
+ String altText = group(1);
+ String href = group(2);
+ String title = group(3);
+ ImageAttributes attributes = new ImageAttributes();
+ attributes.setTitle(title);
+ attributes.setAlt(altText);
+ builder.image(attributes, href);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineLinkReplacementToken.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineLinkReplacementToken.java
new file mode 100644
index 0000000..3b33cba
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/InlineLinkReplacementToken.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.token;
+
+import org.eclipse.mylyn.wikitext.core.parser.LinkAttributes;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElement;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElementProcessor;
+
+/**
+ * Detects inline links: [Text](http://www.example.com "Optional title").
+ *
+ * @author Stefan Seelmann
+ */
+public class InlineLinkReplacementToken extends PatternBasedElement {
+
+ @Override
+ protected String getPattern(int groupOffset) {
+ return "\\[\\s*(.+?)\\s*\\]\\(\\s*(.+?)(?:\\s\"(.+?)\")?\\s*\\)"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected int getPatternGroupCount() {
+ return 3;
+ }
+
+ @Override
+ protected PatternBasedElementProcessor newProcessor() {
+ return new InlineLinkReplacementTokenProcessor();
+ }
+
+ private static class InlineLinkReplacementTokenProcessor extends PatternBasedElementProcessor {
+ @Override
+ public void emit() {
+ String text = group(1);
+ String href = group(2);
+ String title = group(3);
+ LinkAttributes attributes = new LinkAttributes();
+ if (title != null) {
+ attributes.setTitle(title);
+ }
+ builder.link(attributes, href, text);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleImageReplacementToken.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleImageReplacementToken.java
new file mode 100644
index 0000000..bd0cf0f
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleImageReplacementToken.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.token;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinition;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.MarkdownContentState;
+import org.eclipse.mylyn.wikitext.core.parser.ImageAttributes;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElement;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElementProcessor;
+
+/**
+ * Detects reference-style images: ![Alt text][Reference ID].
+ *
+ * @author Stefan Seelmann
+ */
+public class ReferenceStyleImageReplacementToken extends PatternBasedElement {
+
+ @Override
+ protected String getPattern(int groupOffset) {
+ return "!(\\[\\s*(.+?)\\s*\\]\\s*\\[\\s*(.*?)\\s*\\])"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected int getPatternGroupCount() {
+ return 3;
+ }
+
+ @Override
+ protected PatternBasedElementProcessor newProcessor() {
+ return new ReferenceStyleLinkReplacementTokenProcessor();
+ }
+
+ private static class ReferenceStyleLinkReplacementTokenProcessor extends PatternBasedElementProcessor {
+ @Override
+ public void emit() {
+ String altText = group(2);
+ String refid = group(3);
+ if (refid.isEmpty()) {
+ refid = altText;
+ }
+ MarkdownContentState mdContentState = (MarkdownContentState) getState();
+ LinkDefinition linkDefinition = mdContentState.getLinkDefinition(refid);
+ if (linkDefinition != null) {
+ String href = linkDefinition.getUrl();
+ String title = linkDefinition.getTitle();
+ ImageAttributes attributes = new ImageAttributes();
+ attributes.setTitle(title);
+ attributes.setAlt(altText);
+ builder.image(attributes, href);
+ } else {
+ // image definition is missing, just print the raw content
+ builder.characters(group(1));
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleLinkReplacementToken.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleLinkReplacementToken.java
new file mode 100644
index 0000000..793b1e9
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/token/ReferenceStyleLinkReplacementToken.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.token;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinition;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.MarkdownContentState;
+import org.eclipse.mylyn.wikitext.core.parser.LinkAttributes;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElement;
+import org.eclipse.mylyn.wikitext.core.parser.markup.PatternBasedElementProcessor;
+
+/**
+ * Detects reference-style links: [Text][Reference ID].
+ *
+ * @author Stefan Seelmann
+ */
+public class ReferenceStyleLinkReplacementToken extends PatternBasedElement {
+
+ @Override
+ protected String getPattern(int groupOffset) {
+ return "(\\[\\s*(.+?)\\s*\\]\\s*\\[\\s*(.*?)\\s*\\])"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected int getPatternGroupCount() {
+ return 3;
+ }
+
+ @Override
+ protected PatternBasedElementProcessor newProcessor() {
+ return new ReferenceStyleLinkReplacementTokenProcessor();
+ }
+
+ private static class ReferenceStyleLinkReplacementTokenProcessor extends PatternBasedElementProcessor {
+ @Override
+ public void emit() {
+ String text = group(2);
+ String refid = group(3);
+ if (refid.isEmpty()) {
+ refid = text;
+ }
+ MarkdownContentState mdContentState = (MarkdownContentState) getState();
+ LinkDefinition linkDefinition = mdContentState.getLinkDefinition(refid);
+ if (linkDefinition != null) {
+ String href = linkDefinition.getUrl();
+ String title = linkDefinition.getTitle();
+ LinkAttributes attributes = new LinkAttributes();
+ if (title != null) {
+ attributes.setTitle(title);
+ }
+ builder.link(attributes, href, text);
+ } else {
+ // link definition is missing, just print the raw content
+ builder.characters(group(1));
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/util/ReadAheadDispatcher.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/util/ReadAheadDispatcher.java
index 8891a96..37d1662 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/util/ReadAheadDispatcher.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/util/ReadAheadDispatcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,25 +11,29 @@
package org.eclipse.mylyn.internal.wikitext.markdown.core.util;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.block.NestableBlock;
import org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
-import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
import org.eclipse.mylyn.wikitext.core.parser.markup.ContentState;
/**
- * Adapter {@link Block} for {@link ReadAheadBlock}s.
+ * Adapter {@link NestableBlock} for {@link ReadAheadBlock}s.
*
* @author Stefan Seelmann
*/
-public class ReadAheadDispatcher extends Block {
+public class ReadAheadDispatcher extends NestableBlock {
private final LookAheadReader lookAheadReader;
- private Block[] blocks;
+ private List<NestableBlock> blocks;
- private Block dispatchedBlock;
+ private NestableBlock dispatchedBlock;
- public ReadAheadDispatcher(Block... blocks) {
- this.blocks = blocks;
+ public ReadAheadDispatcher(NestableBlock... blocks) {
+ this.blocks = cloneBlocks(Arrays.asList(blocks));
this.lookAheadReader = new LookAheadReader();
}
@@ -43,7 +47,7 @@ public class ReadAheadDispatcher extends Block {
protected int processLineContent(String line, int offset) {
if (dispatchedBlock == null) {
lookAheadReader.setContentState(getState());
- for (Block block : blocks) {
+ for (NestableBlock block : blocks) {
if (block instanceof ReadAheadBlock) {
ReadAheadBlock raBlock = ReadAheadBlock.class.cast(block);
if (raBlock.canStart(line, offset, lookAheadReader)) {
@@ -74,7 +78,7 @@ public class ReadAheadDispatcher extends Block {
@Override
public void setState(ContentState state) {
- for (Block block : blocks) {
+ for (NestableBlock block : blocks) {
block.setState(state);
}
super.setState(state);
@@ -82,22 +86,24 @@ public class ReadAheadDispatcher extends Block {
@Override
public void setParser(MarkupParser parser) {
- for (Block block : blocks) {
+ for (NestableBlock block : blocks) {
block.setParser(parser);
}
super.setParser(parser);
}
@Override
- public Block clone() {
+ public NestableBlock clone() {
ReadAheadDispatcher clone = (ReadAheadDispatcher) super.clone();
- Block[] clonedBlocks = new Block[blocks.length];
- int i = 0;
- for (Block block : blocks) {
- clonedBlocks[i++] = block.clone();
- }
- clone.blocks = clonedBlocks;
+ clone.blocks = cloneBlocks(blocks);
return clone;
}
+ private List<NestableBlock> cloneBlocks(List<NestableBlock> blocks) {
+ List<NestableBlock> clonedBlocks = new ArrayList<NestableBlock>();
+ for (NestableBlock block : blocks) {
+ clonedBlocks.add(block.clone());
+ }
+ return clonedBlocks;
+ }
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/LinkDefinitionValidationRule.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/LinkDefinitionValidationRule.java
new file mode 100644
index 0000000..71b70d6
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/LinkDefinitionValidationRule.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.validation;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinitionUsageTracker;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinitionUsageTracker.Position;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.MarkdownContentState;
+import org.eclipse.mylyn.wikitext.core.parser.Locator;
+import org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
+import org.eclipse.mylyn.wikitext.core.parser.builder.NoOpDocumentBuilder;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem.Severity;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationRule;
+import org.eclipse.mylyn.wikitext.markdown.core.MarkdownLanguage;
+
+/**
+ * {@link ValidationRule} that finds missing and unused link definitions.
+ *
+ * @author Stefan Seelmann
+ */
+public class LinkDefinitionValidationRule extends ValidationRule {
+
+ @Override
+ public ValidationProblem findProblem(String markup, int offset, int length) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ValidationProblem> findProblems(String markup, int offset, int length) {
+
+ MarkupParser parser = new MarkupParser(new MarkdownLanguage());
+ MarkdownContentStateDocumentBuilder builder = new MarkdownContentStateDocumentBuilder();
+ parser.setBuilder(builder);
+ parser.parse(markup);
+
+ MarkdownContentState markdownContentState = builder.markdownContentState;
+ LinkDefinitionUsageTracker linkDefinitionUsageTracker = markdownContentState.getLinkDefinitionUsageTracker();
+
+ List<ValidationProblem> problems = new ArrayList<ValidationProblem>();
+
+ List<Position> missingLinkDefinitionPositions = linkDefinitionUsageTracker.getMissingLinkDefinitionPositions();
+ for (Position position : missingLinkDefinitionPositions) {
+ problems.add(new ValidationProblem(Severity.ERROR, MessageFormat.format(
+ Messages.getString("LinkDefinitionValidationRule.missing"), //$NON-NLS-1$
+ position.getId()), position.getOffset(), position.getLength()));
+ }
+
+ List<Position> unusedLinkDefinitionPositions = linkDefinitionUsageTracker.getUnusedLinkDefinitionPositions();
+ for (Position position : unusedLinkDefinitionPositions) {
+ problems.add(new ValidationProblem(Severity.WARNING, MessageFormat.format(
+ Messages.getString("LinkDefinitionValidationRule.unused"),//$NON-NLS-1$
+ position.getId()), position.getOffset(), position.getLength()));
+ }
+
+ return problems;
+ }
+
+ /**
+ * Document builder that keeps a reference to the set {@link MarkdownContentState}.
+ */
+ private class MarkdownContentStateDocumentBuilder extends NoOpDocumentBuilder {
+
+ MarkdownContentState markdownContentState;
+
+ @Override
+ public void setLocator(Locator locator) {
+ if (locator != null) {
+ markdownContentState = (MarkdownContentState) locator;
+ }
+ super.setLocator(locator);
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/MarkdownReferenceValidationRule.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/MarkdownReferenceValidationRule.java
new file mode 100644
index 0000000..418a5be
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/MarkdownReferenceValidationRule.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.validation;
+
+import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage;
+import org.eclipse.mylyn.wikitext.core.validation.DocumentLocalReferenceValidationRule;
+import org.eclipse.mylyn.wikitext.markdown.core.MarkdownLanguage;
+
+public class MarkdownReferenceValidationRule extends DocumentLocalReferenceValidationRule {
+
+ @Override
+ protected MarkupLanguage createMarkupLanguage() {
+ return new MarkdownLanguage();
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/Messages.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/Messages.java
new file mode 100644
index 0000000..1b4deb8
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/Messages.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.core.validation;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+class Messages {
+ private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.wikitext.markdown.core.validation.messages"; //$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+ private Messages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return RESOURCE_BUNDLE.getString(key);
+ } catch (MissingResourceException e) {
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/messages.properties b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/messages.properties
new file mode 100644
index 0000000..fe1fb9f
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/validation/messages.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2013 Stefan Seelmann
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Stefan Seelmann - initial API and implementation
+###############################################################################
+LinkDefinitionValidationRule.missing=Footnote ''{0}'' is missing.
+LinkDefinitionValidationRule.unused=Footnote ''{0}'' is never used.
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java
index 59be938..54200ca 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -13,20 +13,28 @@ package org.eclipse.mylyn.wikitext.markdown.core;
import java.util.List;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.MarkdownContentState;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.CodeBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.HeadingBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.HorizontalRuleBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.InlineHtmlBlock;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.block.LinkDefinitionBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.ParagraphBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.QuoteBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.block.UnderlinedHeadingBlock;
import org.eclipse.mylyn.internal.wikitext.markdown.core.phrase.BackslashEscapePhraseModifier;
import org.eclipse.mylyn.internal.wikitext.markdown.core.phrase.SimplePhraseModifier;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.AutomaticLinkReplacementToken;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.InlineImageReplacementToken;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.InlineLinkReplacementToken;
import org.eclipse.mylyn.internal.wikitext.markdown.core.token.PreserverHtmlEntityToken;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.ReferenceStyleImageReplacementToken;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.token.ReferenceStyleLinkReplacementToken;
import org.eclipse.mylyn.internal.wikitext.markdown.core.util.ReadAheadDispatcher;
import org.eclipse.mylyn.wikitext.core.parser.DocumentBuilder.SpanType;
import org.eclipse.mylyn.wikitext.core.parser.markup.AbstractMarkupLanguage;
import org.eclipse.mylyn.wikitext.core.parser.markup.Block;
+import org.eclipse.mylyn.wikitext.core.parser.markup.ContentState;
import org.eclipse.mylyn.wikitext.core.parser.markup.phrase.HtmlEndTagPhraseModifier;
import org.eclipse.mylyn.wikitext.core.parser.markup.phrase.HtmlStartTagPhraseModifier;
import org.eclipse.mylyn.wikitext.core.parser.markup.token.PatternLineBreakReplacementToken;
@@ -56,6 +64,13 @@ public class MarkdownLanguage extends AbstractMarkupLanguage {
// inline HTML
phraseModifierSyntax.add(new HtmlEndTagPhraseModifier());
phraseModifierSyntax.add(new HtmlStartTagPhraseModifier());
+ // images
+ phraseModifierSyntax.add(new InlineImageReplacementToken());
+ phraseModifierSyntax.add(new ReferenceStyleImageReplacementToken());
+ // links
+ phraseModifierSyntax.add(new InlineLinkReplacementToken());
+ phraseModifierSyntax.add(new ReferenceStyleLinkReplacementToken());
+ phraseModifierSyntax.add(new AutomaticLinkReplacementToken());
// backslash escaped span elements
phraseModifierSyntax.add(new BackslashEscapePhraseModifier("**")); //$NON-NLS-1$
phraseModifierSyntax.add(new BackslashEscapePhraseModifier("__")); //$NON-NLS-1$
@@ -90,6 +105,7 @@ public class MarkdownLanguage extends AbstractMarkupLanguage {
blocks.add(new HeadingBlock());
blocks.add(new InlineHtmlBlock());
blocks.add(new QuoteBlock());
+ blocks.add(new LinkDefinitionBlock());
}
@Override
@@ -100,4 +116,8 @@ public class MarkdownLanguage extends AbstractMarkupLanguage {
return readAheadBlock;
}
+ @Override
+ protected ContentState createState() {
+ return new MarkdownContentState();
+ }
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionParserTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionParserTest.java
new file mode 100644
index 0000000..aab0d49
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionParserTest.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.tests;
+
+import junit.framework.TestCase;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinition;
+import org.eclipse.mylyn.internal.wikitext.markdown.core.LinkDefinitionParser;
+
+/**
+ * @author Stefan Seelmann
+ */
+public class LinkDefinitionParserTest extends TestCase {
+
+ private LinkDefinitionParser linkDefinitionParser;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ linkDefinitionParser = new LinkDefinitionParser();
+ }
+
+ public void testWithoutTitle() {
+ String markup = "[foo]: http://example.com/";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertNull(linkDefinition.getTitle());
+ }
+
+ public void testDoubleQuotedTitle() {
+ String markup = "[foo]: http://example.com/ \"Optional Title Here\"";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testSingleQuotedTitle() {
+ String markup = "[foo]: http://example.com/ 'Optional Title Here'";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testParenthesedTitle() {
+ String markup = "[foo]: http://example.com/ (Optional Title Here)";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testUrlInAngleBrackets() {
+ String markup = "[foo]: <http://example.com/> (Optional Title Here)";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testTitleCanBePutOnNextLine() {
+ String markup = " [foo]: <http://example.com/>\n (Optional Title Here)";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testMayStartWithUpToThreeSpaces() {
+ String markup = " [foo]: <http://example.com/> (Optional Title Here)";
+ linkDefinitionParser.parse(markup);
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertEquals("Optional Title Here", linkDefinition.getTitle());
+ }
+
+ public void testMayNotStartWithMoreThanThreeSpaces() {
+ String markup = " [foo]: http://example.com/";
+ linkDefinitionParser.parse(markup);
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ }
+
+ public void testNoMatchInMiddleOfLine() {
+ String markup = "Lorem [foo]: http://example.com/ ipsum.";
+ linkDefinitionParser.parse(markup);
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(linkDefinition);
+ }
+
+ public void testNotCaseSensitive() {
+ String markup = "[foo]: http://example.com/";
+ linkDefinitionParser.parse(markup);
+
+ LinkDefinition linkDefinition = linkDefinitionParser.getLinkDefinition("FoO");
+ assertNotNull(linkDefinition);
+ assertEquals("foo", linkDefinition.getId());
+ assertEquals("http://example.com/", linkDefinition.getUrl());
+ assertNull(linkDefinition.getTitle());
+ }
+
+ public void testMultiline() {
+ String markup = "aaa\n\n [foo]: http://foo.com/\n [bar]: http://bar.com/\n\nbbb";
+ linkDefinitionParser.parse(markup);
+ LinkDefinition fooLinkDefinition = linkDefinitionParser.getLinkDefinition("foo");
+ assertNotNull(fooLinkDefinition);
+ assertEquals("foo", fooLinkDefinition.getId());
+ assertEquals("http://foo.com/", fooLinkDefinition.getUrl());
+ assertNull(fooLinkDefinition.getTitle());
+ LinkDefinition barLinkDefinition = linkDefinitionParser.getLinkDefinition("bar");
+ assertNotNull(barLinkDefinition);
+ assertEquals("bar", barLinkDefinition.getId());
+ assertEquals("http://bar.com/", barLinkDefinition.getUrl());
+ assertNull(barLinkDefinition.getTitle());
+ }
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionValidationRuleTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionValidationRuleTest.java
new file mode 100644
index 0000000..7115d77
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/LinkDefinitionValidationRuleTest.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.tests;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.validation.LinkDefinitionValidationRule;
+import org.eclipse.mylyn.wikitext.core.validation.MarkupValidator;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem.Severity;
+import org.eclipse.mylyn.wikitext.tests.TestUtil;
+
+public class LinkDefinitionValidationRuleTest extends TestCase {
+
+ private LinkDefinitionValidationRule rule;
+
+ private MarkupValidator validator;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ rule = new LinkDefinitionValidationRule();
+ validator = new MarkupValidator();
+ validator.getRules().add(rule);
+ }
+
+ public void testNoProblemIfLinkDefinitionExistsWithExplicitLinkName() {
+ final String markup = "[Google][Google]\n\n[Google]: http://www.google.com/";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertTrue(problems.isEmpty());
+ }
+
+ public void testNoProblemIfLinkDefinitionExistsWithImplicitLinkName() {
+ final String markup = "[Bing][]\n\n[Bing]: http://www.bing.com/";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertTrue(problems.isEmpty());
+ }
+
+ public void testErrorIfLinkDefinitionDoesNotExist() {
+ final String markup = "[Yahoo][yahoo]";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(0, problems.get(0).getOffset());
+ assertEquals(14, problems.get(0).getLength());
+ assertEquals(Severity.ERROR, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("yahoo"));
+ assertTrue(problems.get(0).getMessage().contains("missing"));
+ }
+
+ public void testWarningIfLinkDefinitionIsUnused() {
+ final String markup = "[ddg]: https://duckduckgo.com/";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(0, problems.get(0).getOffset());
+ assertEquals(30, problems.get(0).getLength());
+ assertEquals(Severity.WARNING, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("ddg"));
+ assertTrue(problems.get(0).getMessage().contains("never used"));
+ }
+
+ public void testWarningIfLinkDefinitionWithLeadingSpacesIsUnused() {
+ final String markup = " [ddg]: https://duckduckgo.com/";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(2, problems.get(0).getOffset());
+ assertEquals(30, problems.get(0).getLength());
+ assertEquals(Severity.WARNING, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("ddg"));
+ assertTrue(problems.get(0).getMessage().contains("never used"));
+ }
+
+ public void testErrorIfLinkDefinitionDoesNotExistWithinHeading() {
+ final String markup = "# Heading with [Link][link]";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(15, problems.get(0).getOffset());
+ assertEquals(12, problems.get(0).getLength());
+ assertEquals(Severity.ERROR, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("link"));
+ assertTrue(problems.get(0).getMessage().contains("missing"));
+ }
+
+ public void testErrorIfLinkDefinitionDoesNotExistWithinUnderlinedHeading() {
+ final String markup = "Heading with [Link][link]\n=============";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(13, problems.get(0).getOffset());
+ assertEquals(12, problems.get(0).getLength());
+ assertEquals(Severity.ERROR, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("link"));
+ assertTrue(problems.get(0).getMessage().contains("missing"));
+ }
+
+ public void testErrorIfLinkDefinitionDoesNotExistWithinBlockquote() {
+ final String markup = "> [Link][link] within blockquote";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(2, problems.get(0).getOffset());
+ assertEquals(12, problems.get(0).getLength());
+ assertEquals(Severity.ERROR, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("link"));
+ assertTrue(problems.get(0).getMessage().contains("missing"));
+ }
+
+}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java
index 6f84e58..bf57a10 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,6 @@
package org.eclipse.mylyn.internal.wikitext.markdown.tests;
-import org.eclipse.mylyn.wikitext.tests.TestUtil;
/**
* Tests for Markdown block elements. Follows specification at
@@ -27,33 +26,33 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs.
*/
public void testParagraphWithOneLine() {
- String html = parseToHtml("a paragraph");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>a paragraph</p>\n", html);
+ String markup = "a paragraph";
+ String expectedHtml = "<p>a paragraph</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
public void testParagraphWithMulitpleLines() {
- String html = parseToHtml("a paragraph\nwith multiple\nlines");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>a paragraph\nwith multiple\nlines</p>\n", html);
+ String markup = "a paragraph\nwith multiple\nlines";
+ String expectedHtml = "<p>a paragraph\nwith multiple\nlines</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
public void testParagraphsSeparatedBySingleBlankLine() {
- String html = parseToHtml("a paragraph\n\nanother paragraph\n\n");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>a paragraph</p>\n<p>another paragraph</p>\n", html);
+ String markup = "a paragraph\n\nanother paragraph\n\n";
+ String expectedHtml = "<p>a paragraph</p>\n<p>another paragraph</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
public void testParagraphsSeparatedByMulitpleBlankLines() {
- String html = parseToHtml("a paragraph\n\n\nanother paragraph\n\n\n");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>a paragraph</p>\n<p>another paragraph</p>\n", html);
+ String markup = "a paragraph\n\n\nanother paragraph\n\n\n";
+ String expectedHtml = "<p>a paragraph</p>\n<p>another paragraph</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
public void testParagraphsSeparatedByMulitpleBlankLinesWithSpacesAndTabs() {
- String html = parseToHtml("a paragraph\n \n\t\nanother paragraph");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>a paragraph</p>\n<p>another paragraph</p>\n", html);
+ String markup = "a paragraph\n \n\t\nanother paragraph";
+ String expectedHtml = "<p>a paragraph</p>\n<p>another paragraph</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -61,48 +60,48 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* break tag using Markdown, you end a line with two or more spaces, then type return.
*/
public void testLineBreakInParagraph() {
- String html = parseToHtml("line 1 \nline 2 \nline 3");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>line 1<br/>\nline 2<br/>\nline 3</p>\n", html);
+ String markup = "line 1 \nline 2 \nline 3";
+ String expectedHtml = "<p>line 1<br/>\nline 2<br/>\nline 3</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Headers. Atx-style headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6.
*/
public void testAtxStyleHeaderH1() {
- String h1 = parseToHtml("# This is an H1");
- TestUtil.println("HTML: " + h1);
- assertEquals("<h1>This is an H1</h1>", h1);
+ String markup = "# This is an H1";
+ String expectedHtml = "<h1>This is an H1</h1>";
+ parseAndAssert(markup, expectedHtml);
}
public void testAtxStyleHeaderH2() {
- String h2 = parseToHtml("## This is an H2");
- TestUtil.println("HTML: " + h2);
- assertEquals("<h2>This is an H2</h2>", h2);
+ String markup = "## This is an H2";
+ String expectedHtml = "<h2>This is an H2</h2>";
+ parseAndAssert(markup, expectedHtml);
}
public void testAtxStyleHeaderH3() {
- String h3 = parseToHtml("### This is an H3");
- TestUtil.println("HTML: " + h3);
- assertEquals("<h3>This is an H3</h3>", h3);
+ String markup = "### This is an H3";
+ String expectedHtml = "<h3>This is an H3</h3>";
+ parseAndAssert(markup, expectedHtml);
}
public void testAtxStyleHeaderH4() {
- String h4 = parseToHtml("#### This is an H4");
- TestUtil.println("HTML: " + h4);
- assertEquals("<h4>This is an H4</h4>", h4);
+ String markup = "#### This is an H4";
+ String expectedHtml = "<h4>This is an H4</h4>";
+ parseAndAssert(markup, expectedHtml);
}
public void testAtxStyleHeaderH5() {
- String h5 = parseToHtml("##### This is an H5");
- TestUtil.println("HTML: " + h5);
- assertEquals("<h5>This is an H5</h5>", h5);
+ String markup = "##### This is an H5";
+ String expectedHtml = "<h5>This is an H5</h5>";
+ parseAndAssert(markup, expectedHtml);
}
public void testAtxStyleHeaderH6() {
- String h6 = parseToHtml("###### This is an H6");
- TestUtil.println("HTML: " + h6);
- assertEquals("<h6>This is an H6</h6>", h6);
+ String markup = "###### This is an H6";
+ String expectedHtml = "<h6>This is an H6</h6>";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -111,51 +110,51 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* opening hashes determines the header level.)
*/
public void testClosedAtxStyleHeaderH1() {
- String h1 = parseToHtml("# This is an H1 #");
- TestUtil.println("HTML: " + h1);
- assertEquals("<h1>This is an H1</h1>", h1);
+ String markup = "# This is an H1 #";
+ String expectedHtml = "<h1>This is an H1</h1>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderH2() {
- String h2 = parseToHtml("## This is an H2 ##");
- TestUtil.println("HTML: " + h2);
- assertEquals("<h2>This is an H2</h2>", h2);
+ String markup = "## This is an H2 ##";
+ String expectedHtml = "<h2>This is an H2</h2>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderH3() {
- String h3 = parseToHtml("### This is an H3 ###");
- TestUtil.println("HTML: " + h3);
- assertEquals("<h3>This is an H3</h3>", h3);
+ String markup = "### This is an H3 ###";
+ String expectedHtml = "<h3>This is an H3</h3>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderH4() {
- String h4 = parseToHtml("#### This is an H4 ####");
- TestUtil.println("HTML: " + h4);
- assertEquals("<h4>This is an H4</h4>", h4);
+ String markup = "#### This is an H4 ####";
+ String expectedHtml = "<h4>This is an H4</h4>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderH5() {
- String h5 = parseToHtml("##### This is an H5 #####");
- TestUtil.println("HTML: " + h5);
- assertEquals("<h5>This is an H5</h5>", h5);
+ String markup = "##### This is an H5 #####";
+ String expectedHtml = "<h5>This is an H5</h5>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderH6() {
- String h6 = parseToHtml("###### This is an H6 ######");
- TestUtil.println("HTML: " + h6);
- assertEquals("<h6>This is an H6</h6>", h6);
+ String markup = "###### This is an H6 ######";
+ String expectedHtml = "<h6>This is an H6</h6>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderWithMoreClosingHashes() {
- String h1 = parseToHtml("# This is an H1 ################################");
- TestUtil.println("HTML: " + h1);
- assertEquals("<h1>This is an H1</h1>", h1);
+ String markup = "# This is an H1 ################################";
+ String expectedHtml = "<h1>This is an H1</h1>";
+ parseAndAssert(markup, expectedHtml);
}
public void testClosedAtxStyleHeaderWithLessCosingHashes() {
- String h6 = parseToHtml("###### This is an H6 #");
- TestUtil.println("HTML: " + h6);
- assertEquals("<h6>This is an H6</h6>", h6);
+ String markup = "###### This is an H6 #";
+ String expectedHtml = "<h6>This is an H6</h6>";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -163,27 +162,27 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* headers). Any number of underlining ='s or -'s will work.
*/
public void testUnderlinedHeaderH1() {
- String h1 = parseToHtml("This is an H1\n============");
- TestUtil.println("HTML: " + h1);
- assertEquals("<h1>This is an H1</h1>", h1);
+ String markup = "This is an H1\n============";
+ String expectedHtml = "<h1>This is an H1</h1>";
+ parseAndAssert(markup, expectedHtml);
}
public void testUnderlinedHeaderH2() {
- String h2 = parseToHtml("This is an H2\n------------");
- TestUtil.println("HTML: " + h2);
- assertEquals("<h2>This is an H2</h2>", h2);
+ String markup = "This is an H2\n------------";
+ String expectedHtml = "<h2>This is an H2</h2>";
+ parseAndAssert(markup, expectedHtml);
}
public void testSingleCharUnderlinedHeaderH1() {
- String h1 = parseToHtml("This is an H1\n= ");
- TestUtil.println("HTML: " + h1);
- assertEquals("<h1>This is an H1</h1>", h1);
+ String markup = "This is an H1\n= ";
+ String expectedHtml = "<h1>This is an H1</h1>";
+ parseAndAssert(markup, expectedHtml);
}
public void testSingleCharUnderlinedHeaderH2() {
- String h2 = parseToHtml("This is an H2\n- ");
- TestUtil.println("HTML: " + h2);
- assertEquals("<h2>This is an H2</h2>", h2);
+ String markup = "This is an H2\n- ";
+ String expectedHtml = "<h2>This is an H2</h2>";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -191,36 +190,102 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* put a > before every line.
*/
public void testBlockquoteWithQuoteCharInEachLine() {
- String h1 = parseToHtml("> Lorem ipsum dolor sit amet, \n> consetetur adipisici elit.\n");
- TestUtil.println("HTML: " + h1);
- assertEquals("<blockquote><p>Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.</p>\n</blockquote>", h1);
+ String markup = "> Lorem ipsum dolor sit amet, \n> consetetur adipisici elit.\n";
+ String expectedHtml = "<blockquote><p>Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Markdown allows you to be lazy and only put the > before the first line of a hard-wrapped paragraph.
*/
public void testBlockquoteWithSingleQuoteChar() {
- String h1 = parseToHtml("> Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.\n");
- TestUtil.println("HTML: " + h1);
- assertEquals("<blockquote><p>Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.</p>\n</blockquote>", h1);
+ String markup = "> Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.\n";
+ String expectedHtml = "<blockquote><p>Lorem ipsum dolor sit amet, \nconsetetur adipisici elit.</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of >.
*/
- public void testNestedBlockquotes() {
- String h1 = parseToHtml(">A1\n>>B1\n>A2\n");
- TestUtil.println("HTML: " + h1);
- assertEquals("<blockquote><p>A1</p>\n<blockquote><p>B1</p>\n</blockquote><p>A2</p>\n</blockquote>", h1);
+ public void testNestedBlockquotesTwoLevels() {
+ String markup = "> A1\n>\n> > B1\n> > B2\n>\n> A2\n";
+ String expectedHtml = "<blockquote><p>A1</p>\n<blockquote><p>B1\nB2</p>\n</blockquote><p>A2</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNestedBlockquotesThreeLevels() {
+ String markup = "> A1\n>\n> > B1\n> >\n> > > C1\n>\n> A2\n";
+ String expectedHtml = "<blockquote><p>A1</p>\n<blockquote><p>B1</p>\n<blockquote><p>C1</p>\n</blockquote></blockquote><p>A2</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Blockquotes can contain other Markdown elements, including headers, lists, and code blocks.
*/
- public void testBlockquotesWithOtherElements() {
- String h1 = parseToHtml(">#H1");
- TestUtil.println("HTML: " + h1);
- assertEquals("<blockquote><h1>H1</h1></blockquote>", h1);
+ public void testBlockquotesContainingParagraphs() {
+ String markup = ">a\n>b\n>\n>c";
+ String expectedHtml = "<blockquote><p>a\nb</p>\n<p>c</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingHeader() {
+ String markup = ">#H1";
+ String expectedHtml = "<blockquote><h1>H1</h1></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingUnderlinedHeader1() {
+ String markup = ">H1\n>===";
+ String expectedHtml = "<blockquote><h1>H1</h1></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingUnderlinedHeader2() {
+ String markup = ">H2\n>---";
+ String expectedHtml = "<blockquote><h2>H2</h2></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingInlineLink() {
+ String markup = ">[Link](http://www.example.com)";
+ String expectedHtml = "<blockquote><p><a href=\"http://www.example.com\">Link</a></p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingReferenceLink() {
+ String markup = ">[Link][link]\n>\n>[link]: http://www.example.com";
+ String expectedHtml = "<blockquote><p><a href=\"http://www.example.com\">Link</a></p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingHorizontalRule() {
+ String markup = ">---";
+ String expectedHtml = "<blockquote><hr/></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingHorizontalRuleIsNotInterpretedAsUnderlinedHeader() {
+ String markup = ">No H2.\n>\n>---\n";
+ String expectedHtml = "<blockquote><p>No H2.</p>\n<hr/></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingCodeBlock() {
+ String markup = "> code\n> block";
+ String expectedHtml = "<blockquote><pre><code>code\nblock</code></pre></blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquotesContainingInlineHTML() {
+ String markup = "> <input type=\"button\" value=\"Click\"/>";
+ String expectedHtml = "<blockquote><input type=\"button\" value=\"Click\"/>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testBlockquoteSimple() {
+ String markup = "> a\n> b";
+ String expectedHtml = "<blockquote><p>a\nb</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -228,51 +293,48 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* line of the block by at least 4 spaces or 1 tab.
*/
public void testCodeBlockIndentedByFourSpaces() {
- String html = parseToHtml(" This is a code block.");
- TestUtil.println("HTML: " + html);
- assertEquals("<pre><code>This is a code block.</code></pre>", html);
+ String markup = " This is a code block.";
+ String expectedHtml = "<pre><code>This is a code block.</code></pre>";
+ parseAndAssert(markup, expectedHtml);
}
public void testCodeBlockIndentedByOneTab() {
- String html = parseToHtml("\tThis is a code block.");
- TestUtil.println("HTML: " + html);
- assertEquals("<pre><code>This is a code block.</code></pre>", html);
+ String markup = "\tThis is a code block.";
+ String expectedHtml = "<pre><code>This is a code block.</code></pre>";
+ parseAndAssert(markup, expectedHtml);
}
/*
* One level of indentation - 4 spaces or 1 tab - is removed from each line of the code block.
*/
public void testCodeBlockMultiLineIndentedByFourSpaces() {
- String html = parseToHtml(" aaa\n bbb\n ccc\n \n continue after empty line");
- TestUtil.println("HTML: " + html);
+ String markup = " aaa\n bbb\n ccc\n \n continue after empty line";
String expectedHtml = "<pre><code>aaa\n bbb\n ccc\n\ncontinue after empty line</code></pre>";
- assertEquals(expectedHtml, html);
+ parseAndAssert(markup, expectedHtml);
}
public void testCodeBlockMultiLineIndentedByOneTab() {
- String html = parseToHtml("\taaa\n\t\tbbb\n\t\t\tccc\n\t\n\tcontinue after empty line");
- TestUtil.println("HTML: " + html);
+ String markup = "\taaa\n\t\tbbb\n\t\t\tccc\n\t\n\tcontinue after empty line";
String expectedHtml = "<pre><code>aaa\n bbb\n ccc\n\ncontinue after empty line</code></pre>";
- assertEquals(expectedHtml, html);
+ parseAndAssert(markup, expectedHtml);
}
/*
* Within a code block, ampersands (&) and angle brackets (< and >) are automatically converted into HTML entities.
*/
public void testSpecialCharactersAreConvertedInCodeBlock() {
- String html = parseToHtml(" <div class=\"footer\">\n &copy; 2004 Foo Bar\n </div>");
- TestUtil.println("HTML: " + html);
- String exptectedHtml = "<pre><code>&lt;div class=\"footer\"&gt;\n&amp;copy; 2004 Foo Bar\n&lt;/div&gt;</code></pre>";
- assertEquals(exptectedHtml, html);
+ String markup = " <div class=\"footer\">\n &copy; 2004 Foo Bar\n </div>";
+ String expectedHtml = "<pre><code>&lt;div class=\"footer\"&gt;\n&amp;copy; 2004 Foo Bar\n&lt;/div&gt;</code></pre>";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Regular Markdown syntax is not processed within code blocks.
*/
public void testNoProcessingInCodeBlock() {
- String html = parseToHtml(" ### Header 3\n Lorem *ipsum*");
- TestUtil.println("HTML: " + html);
- assertEquals("<pre><code>### Header 3\nLorem *ipsum*</code></pre>", html);
+ String markup = " ### Header 3\n Lorem *ipsum*";
+ String expectedHtml = "<pre><code>### Header 3\nLorem *ipsum*</code></pre>";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -280,38 +342,26 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase
* underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks.
*/
public void testHorizontalRulesWithAsterisksAndSpaces() {
- String html = parseToHtml("* * *");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("* * *", "<hr/>");
}
public void testHorizontalRulesWithAsterisks() {
- String html = parseToHtml("***");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("***", "<hr/>");
}
public void testHorizontalRulesWithMoreAsterisks() {
- String html = parseToHtml("*****");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("*****", "<hr/>");
}
public void testHorizontalRulesWithHyphensAndSpaces() {
- String html = parseToHtml("- - -");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("- - -", "<hr/>");
}
public void testHorizontalRulesWithHyphens() {
- String html = parseToHtml("---------------------------------------");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("---------------------------------------", "<hr/>");
}
public void testHorizontalRulesWithUnderscores() {
- String html = parseToHtml("___");
- TestUtil.println("HTML: " + html);
- assertEquals("<hr/>", html);
+ parseAndAssert("___", "<hr/>");
}
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageMiscellaneousTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageMiscellaneousTest.java
index 8ee7c3b..789ad0a 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageMiscellaneousTest.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageMiscellaneousTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,6 @@
package org.eclipse.mylyn.internal.wikitext.markdown.tests;
-import org.eclipse.mylyn.wikitext.tests.TestUtil;
/**
* Tests for Markdown overview and miscellaneous. Follows specification at
@@ -23,9 +22,7 @@ import org.eclipse.mylyn.wikitext.tests.TestUtil;
public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase {
public void testEmptyLine() {
- String html = parseToHtml(" ");
- TestUtil.println("HTML: " + html);
- assertEquals("", html);
+ parseAndAssert(" ", "");
}
/*
@@ -36,9 +33,9 @@ public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase
* spaces. Markdown is smart enough not to add extra (unwanted) p tags around HTML block-level tags.
*/
public void testInlineHtml() throws Exception {
- String html = parseToHtml("aaa\n\n<table>\n <tr>\n <td>Foo</td>\n </tr>\n</table>\n\nbbb");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>aaa</p>\n<table>\n <tr>\n <td>Foo</td>\n </tr>\n</table>\n<p>bbb</p>\n", html);
+ String markup = "aaa\n\n<table>\n <tr>\n <td>Foo</td>\n </tr>\n</table>\n\nbbb";
+ String expectedHtml = "<p>aaa</p>\n<table>\n <tr>\n <td>Foo</td>\n </tr>\n</table>\n<p>bbb</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -46,9 +43,9 @@ public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase
* Markdown-style *emphasis* inside an HTML block.
*/
public void testNoProcessingWithinInlineHtmlBlockLevelTags() throws Exception {
- String html = parseToHtml("<div>*Foo*</div>");
- TestUtil.println("HTML: " + html);
- assertEquals("<div>*Foo*</div>\n", html);
+ String markup = "<div>*Foo*</div>";
+ String expectedHtml = "<div>*Foo*</div>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -57,19 +54,18 @@ public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase
* a or img tags instead of Markdown's link or image syntax, go right ahead.
*/
public void testSpanLevelTags() throws Exception {
- String html = parseToHtml("Image: <img src=\"image.jpg\">some nice image</img>.");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>Image: <img src=\"image.jpg\">some nice image</img>.</p>\n", html);
-
+ String markup = "Image: <img src=\"image.jpg\">some nice image</img>.";
+ String expectedHtml = "<p>Image: <img src=\"image.jpg\">some nice image</img>.</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.
*/
public void testProcessingInSpanLevelTags() throws Exception {
- String html = parseToHtml("Image: <img src=\"image.jpg\">some **nice** image</img>.");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>Image: <img src=\"image.jpg\">some <strong>nice</strong> image</img>.</p>\n", html);
+ String markup = "Image: <img src=\"image.jpg\">some **nice** image</img>.";
+ String expectedHtml = "<p>Image: <img src=\"image.jpg\">some <strong>nice</strong> image</img>.</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -79,36 +75,32 @@ public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase
* can write: &copy; and Markdown will leave it alone.
*/
public void testPreserveHtmlEntities() {
- String copy = parseToHtml("&copy; &amp;");
- TestUtil.println("HTML: " + copy);
- assertEquals("<p>&copy; &amp;</p>\n", copy);
+ parseAndAssert("&copy; &amp;", "<p>&copy; &amp;</p>\n");
}
/*
* But if you write: AT&T Markdown will translate it to: AT&amp;T.
*/
public void testAmpersandIsEscaped() {
- String amp = parseToHtml("AT&T, a & b");
- TestUtil.println("HTML: " + amp);
- assertEquals("<p>AT&amp;T, a &amp; b</p>\n", amp);
+ parseAndAssert("AT&T, a & b", "<p>AT&amp;T, a &amp; b</p>\n");
+ }
- String urlWithAmp = parseToHtml("http://images.google.com/images?num=30&q=larry+bird");
- TestUtil.println("HTML: " + urlWithAmp);
- assertEquals("<p>http://images.google.com/images?num=30&amp;q=larry+bird</p>\n", urlWithAmp);
+ public void testAmpersandIsEscapedWithinUrl() {
+ String markup = "http://images.google.com/images?num=30&q=larry+bird";
+ String expectedHtml = "<p>http://images.google.com/images?num=30&amp;q=larry+bird</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
* Similarly, because Markdown supports inline HTML, if you use angle brackets as delimiters for HTML tags, Markdown
* will treat them as such. But if you write: 4 < 5 Markdown will translate it to: 4 &lt; 5
*/
- public void testAngleBracketsAreEscaped() {
- String lt = parseToHtml("4 < 5");
- TestUtil.println("HTML: " + lt);
- assertEquals("<p>4 &lt; 5</p>\n", lt);
+ public void testLessThanAngleBracketIsEscaped() {
+ parseAndAssert("4 < 5", "<p>4 &lt; 5</p>\n");
+ }
- String gt = parseToHtml("6 > 5");
- TestUtil.println("HTML: " + gt);
- assertEquals("<p>6 &gt; 5</p>\n", gt);
+ public void testGreaterThanAngleBracketIsEscaped() {
+ parseAndAssert("6 > 5", "<p>6 &gt; 5</p>\n");
}
/*
@@ -116,63 +108,85 @@ public class MarkdownLanguageMiscellaneousTest extends MarkdownLanguageTestBase
* meaning in Markdown's formatting syntax.
*/
public void testEscapedBackslash() {
- assertEquals("<p>\\</p>\n", parseToHtml("\\\\"));
+ parseAndAssert("\\\\", "<p>\\</p>\n");
}
public void testEscapedBacktick() {
- assertEquals("<p>`</p>\n", parseToHtml("\\`"));
+ parseAndAssert("\\`", "<p>`</p>\n");
}
public void testEscapedAsterisk() {
- assertEquals("<p>*</p>\n", parseToHtml("\\*"));
+ parseAndAssert("\\*", "<p>*</p>\n");
}
public void testEscapedUnderscore() {
- assertEquals("<p>_</p>\n", parseToHtml("\\_"));
+ parseAndAssert("\\_", "<p>_</p>\n");
}
public void testEscapedOpeningCurlyBrace() {
- assertEquals("<p>{</p>\n", parseToHtml("\\{"));
+ parseAndAssert("\\{", "<p>{</p>\n");
}
public void testEscapedClosingCurlyBrace() {
- assertEquals("<p>}</p>\n", parseToHtml("\\}"));
+ parseAndAssert("\\}", "<p>}</p>\n");
}
public void testEscapedOpeningSquareBracket() {
- assertEquals("<p>[</p>\n", parseToHtml("\\["));
+ parseAndAssert("\\[", "<p>[</p>\n");
}
public void testEscapedClosingSquareBracket() {
- assertEquals("<p>]</p>\n", parseToHtml("\\]"));
+ parseAndAssert("\\]", "<p>]</p>\n");
}
public void testEscapedOpeningParenthesis() {
- assertEquals("<p>(</p>\n", parseToHtml("\\("));
+ parseAndAssert("\\(", "<p>(</p>\n");
}
public void testEscapedClosingParenthesis() {
- assertEquals("<p>)</p>\n", parseToHtml("\\)"));
+ parseAndAssert("\\)", "<p>)</p>\n");
}
public void testEscapedHashMark() {
- assertEquals("<p>#</p>\n", parseToHtml("\\#"));
+ parseAndAssert("\\#", "<p>#</p>\n");
}
public void testEscapedPlusSign() {
- assertEquals("<p>+</p>\n", parseToHtml("\\+"));
+ parseAndAssert("\\+", "<p>+</p>\n");
}
public void testEscapedMinusSign() {
- assertEquals("<p>-</p>\n", parseToHtml("\\-"));
+ parseAndAssert("\\-", "<p>-</p>\n");
}
public void testEscapedDot() {
- assertEquals("<p>.</p>\n", parseToHtml("\\."));
+ parseAndAssert("\\.", "<p>.</p>\n");
}
public void testEscapedExclamationMark() {
- assertEquals("<p>!</p>\n", parseToHtml("\\!"));
+ parseAndAssert("\\!", "<p>!</p>\n");
+ }
+
+ /*
+ * Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround
+ * the URL or email address with angle brackets. What this means is that if you want to show the actual text of a
+ * URL or email address, and also have it be a clickable link, you can do this:
+ */
+ public void testAutomaticLinksAtBeginOfLine() throws Exception {
+ String markup = "<http://example.com/>";
+ String expectedHtml = "<p><a href=\"http://example.com/\">http://example.com/</a></p>\n";
+ parseAndAssert(markup, expectedHtml);
}
+ public void testAutomaticLinksWithinText() throws Exception {
+ String markup = "This <http://example.com/> is an automatic link.";
+ String expectedHtml = "<p>This <a href=\"http://example.com/\">http://example.com/</a> is an automatic link.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoSpanWithinAutomaticLinks() throws Exception {
+ String markup = "This <http://www.google.de/?q=t_es_t> is an automatic link.";
+ String expectedHtml = "<p>This <a href=\"http://www.google.de/?q=t_es_t\">http://www.google.de/?q=t_es_t</a> is an automatic link.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageSpanElementsTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageSpanElementsTest.java
index 4ab4813..591377e 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageSpanElementsTest.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageSpanElementsTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,6 @@
package org.eclipse.mylyn.internal.wikitext.markdown.tests;
-import org.eclipse.mylyn.wikitext.tests.TestUtil;
/**
* Tests for Markdown span elements. Follows specification at
@@ -22,49 +21,218 @@ import org.eclipse.mylyn.wikitext.tests.TestUtil;
public class MarkdownLanguageSpanElementsTest extends MarkdownLanguageTestBase {
/*
+ * Links. To create an inline link, use a set of regular parentheses immediately after the link text's
+ * closing square bracket. Inside the parentheses, put the URL where you want the link to point, along
+ * with an optional title for the link, surrounded in quotes.
+ */
+ public void testInlineLinkWithTitle() {
+ String markup = "This is [ an example ](http://example.com/ \"Title\") inline link.";
+ String expectedHtml = "<p>This is <a href=\"http://example.com/\" title=\"Title\">an example</a> inline link.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testInlineLinkWithoutTitle() {
+ String markup = "[This link](http://example.net/) has no title attribute.";
+ String expectedHtml = "<p><a href=\"http://example.net/\">This link</a> has no title attribute.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * If you're referring to a local resource on the same server, you can use relative paths:
+ */
+ public void testInlineLinkAsRelativePath() {
+ String markup = "See my [About](/about/) page for details.";
+ String expectedHtml = "<p>See my <a href=\"/about/\">About</a> page for details.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoSpanWithinInlineLinks() throws Exception {
+ String markup = "[Test](http://www.google.de/?q=t_es_t)";
+ String expectedHtml = "<p><a href=\"http://www.google.de/?q=t_es_t\">Test</a></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * Reference-style links use a second set of square brackets, inside which you place a label of your choosing to
+ * identify the link. Then, anywhere in the document, you define your link label like this, on a line by itself.
+ * Link definitions are only used for creating links during Markdown processing, and are stripped from your
+ * document in the HTML output.
+ * Note: Reference parsing is tested in LinkReferencesTest.
+ */
+ public void testReferenceStyleLink() {
+ String markup = "This is [an example][id] reference-style link.\n\n[id]: http://example.com/ \"Optional Title Here\"";
+ String expectedHtml = "<p>This is <a href=\"http://example.com/\" title=\"Optional Title Here\">an example</a> reference-style link.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * You can optionally use a space to separate the sets of brackets
+ */
+ public void testReferenceStyleLinkWithSpace() {
+ String markup = "This is [an example] [id] reference-style link.\n\n[id]: http://example.com/ \"Optional Title Here\"";
+ String expectedHtml = "<p>This is <a href=\"http://example.com/\" title=\"Optional Title Here\">an example</a> reference-style link.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * Link definition names may consist of letters, numbers, spaces, and punctuation.
+ */
+ public void testReferenceStyleLinkNameAllowedCharacters() {
+ String markup = "[Link][A-Z 1.0]\n\n[A-Z 1.0]: http://example.com/";
+ String expectedHtml = "<p><a href=\"http://example.com/\">Link</a></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * But they are not case sensitive. E.g. these two links: [link text][a] and [link text][A] are equivalent.
+ */
+ public void testReferenceStyleLinkNameIsNotCaseSensitive() {
+ String markup = "[link text a][a] and [LINK TEXT A][A].\n\n[A]: http://example.com/";
+ String expectedHtml = "<p><a href=\"http://example.com/\">link text a</a> and <a href=\"http://example.com/\">LINK TEXT A</a>.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * The implicit link name shortcut allows you to omit the name of the link, in which case the link text itself
+ * is used as the name. Just use an empty set of square brackets.
+ */
+ public void testReferenceStyleLinkWithoutName() {
+ String markup = "[Google][]\n\n[Google]: http://google.com/";
+ String expectedHtml = "<p><a href=\"http://google.com/\">Google</a></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * Because link names may contain spaces, this shortcut even works for multiple words in the link text.
+ */
+ public void testReferenceStyleLinkWithoutName2() {
+ String markup = "Visit [Daring Fireball][] for more information.\n\n[Daring Fireball]: http://daringfireball.net/";
+ String expectedHtml = "<p>Visit <a href=\"http://daringfireball.net/\">Daring Fireball</a> for more information.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testReferenceStyleLinkWithMissingReference() {
+ String markup = "This is a [link][a] with missing reference.\n\n[b]: http://example.com/";
+ String expectedHtml = "<p>This is a [link][a] with missing reference.</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoSpanWithinReferenceStyleLinks() throws Exception {
+ String markup = "[Test][test]\n\n[test]: http://www.google.de/?q=t_es_t";
+ String expectedHtml = "<p><a href=\"http://www.google.de/?q=t_es_t\">Test</a></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoLinkProcessingWithinCodeBlock() throws Exception {
+ String markup = " Code block [no link](http://example.com/).";
+ String expectedHtml = "<pre><code>Code block [no link](http://example.com/).</code></pre>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoLinkProcessingWithinInlineHtmlBlock() throws Exception {
+ String markup = "<p>Inline html [no link](http://example.com/).</p>";
+ String expectedHtml = "<p>Inline html [no link](http://example.com/).</p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testInlineLinkProcessingWithinAtxStyleHeading() throws Exception {
+ String markup = "# Heading with [link](http://example.com/).";
+ String expectedHtml = "<h1>Heading with <a href=\"http://example.com/\">link</a>.</h1>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testReferenceLinkProcessingWithinAtxStyleHeading() throws Exception {
+ String markup = "# Heading with [link][].\n\n[link]: http://example.com/";
+ String expectedHtml = "<h1>Heading with <a href=\"http://example.com/\">link</a>.</h1>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testInlineLinkProcessingWithinUnderlinedHeading() throws Exception {
+ String markup = "Heading with [link](http://example.com/).\n===";
+ String expectedHtml = "<h1>Heading with <a href=\"http://example.com/\">link</a>.</h1>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testReferenceLinkProcessingWithinUnderlinedHeading() throws Exception {
+ String markup = "Heading with [link][].\n===\n\n[link]: http://example.com/";
+ String expectedHtml = "<h1>Heading with <a href=\"http://example.com/\">link</a>.</h1>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testLinkProcessingWithinBlockQuote() throws Exception {
+ String markup = "> Block quote [link](http://example.com/).";
+ String expectedHtml = "<blockquote><p>Block quote <a href=\"http://example.com/\">link</a>.</p>\n</blockquote>";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * Inline image syntax
+ */
+ public void testInlineImageWithTitle() {
+ String markup = "![Alt text](/path/to/img.jpg \"Optional title\")";
+ String expectedHtml = "<p><img alt=\"Alt text\" title=\"Optional title\" border=\"0\" src=\"/path/to/img.jpg\"/></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testInlineImageWithoutTitle() {
+ String markup = "![Alt text](/path/to/img.jpg)";
+ String expectedHtml = "<p><img alt=\"Alt text\" border=\"0\" src=\"/path/to/img.jpg\"/></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoSpanWithinInlineImage() throws Exception {
+ String markup = "![Alt text](/path/to/my_nice_image.jpg)";
+ String expectedHtml = "<p><img alt=\"Alt text\" border=\"0\" src=\"/path/to/my_nice_image.jpg\"/></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
+ * Reference-style image syntax
+ */
+ public void testReferenceStyleImage() {
+ String markup = "![Alt text][id]\n\n[id]: url/to/image \"Optional title attribute\"";
+ String expectedHtml = "<p><img alt=\"Alt text\" title=\"Optional title attribute\" border=\"0\" src=\"url/to/image\"/></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ public void testNoSpanWithinReferenceStyleImage() {
+ String markup = "![Alt text][id]\n\n[id]: /path/to/my_nice_image.jpg";
+ String expectedHtml = "<p><img alt=\"Alt text\" border=\"0\" src=\"/path/to/my_nice_image.jpg\"/></p>\n";
+ parseAndAssert(markup, expectedHtml);
+ }
+
+ /*
* Emphasis. Markdown treats asterisks * and underscores _ as indicators of emphasis. Text wrapped with one * or _ will be
* wrapped with an HTML em tag; double **'s or __ will be wrapped with an HTML strong tag.
*/
public void testEmphasisWithAsterisks() {
- String html = parseToHtml("*foo bar*");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><em>foo bar</em></p>\n", html);
+ parseAndAssert("*foo bar*", "<p><em>foo bar</em></p>\n");
}
public void testEmphasisWithUnderscore() {
- String html = parseToHtml("_foo bar_");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><em>foo bar</em></p>\n", html);
+ parseAndAssert("_foo bar_", "<p><em>foo bar</em></p>\n");
}
public void testStrongWithAsterisks() {
- String html = parseToHtml("**foo bar**");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><strong>foo bar</strong></p>\n", html);
+ parseAndAssert("**foo bar**", "<p><strong>foo bar</strong></p>\n");
}
public void testStrongWithUnderscore() {
- String html = parseToHtml("__foo bar__");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><strong>foo bar</strong></p>\n", html);
+ parseAndAssert("__foo bar__", "<p><strong>foo bar</strong></p>\n");
}
/*
* Emphasis can be used in the middle of a word.
*/
public void testEmphasisWithinWord() {
- String html = parseToHtml("un*frigging*believable");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>un<em>frigging</em>believable</p>\n", html);
+ parseAndAssert("un*frigging*believable", "<p>un<em>frigging</em>believable</p>\n");
}
/*
* But if you surround an * or _ with spaces, it'll be treated as a literal asterisk or underscore.
*/
public void testLiteralAsteriskAndUnderscore() {
- String html = parseToHtml("asterisk * underscore _");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>asterisk * underscore _</p>\n", html);
+ parseAndAssert("asterisk * underscore _", "<p>asterisk * underscore _</p>\n");
}
/*
@@ -72,36 +240,28 @@ public class MarkdownLanguageSpanElementsTest extends MarkdownLanguageTestBase {
* delimiter, you can backslash escape it.
*/
public void testEscapesAsterisk() {
- String html = parseToHtml("\\*foo bar\\*");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>*foo bar*</p>\n", html);
+ parseAndAssert("\\*foo bar\\*", "<p>*foo bar*</p>\n");
}
public void testEscapesUnderscore() {
- String html = parseToHtml("\\_foo bar\\_");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>_foo bar_</p>\n", html);
+ parseAndAssert("\\_foo bar\\_", "<p>_foo bar_</p>\n");
}
public void testEscapesDoubleAsterisk() {
- String html = parseToHtml("\\**foo bar\\**");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>**foo bar**</p>\n", html);
+ parseAndAssert("\\**foo bar\\**", "<p>**foo bar**</p>\n");
}
public void testEscapesDoubleUnderscore() {
- String html = parseToHtml("\\__foo bar\\__");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>__foo bar__</p>\n", html);
+ parseAndAssert("\\__foo bar\\__", "<p>__foo bar__</p>\n");
}
/*
* Code. To indicate a span of code, wrap it with backtick quotes.
*/
public void testCodeSpan() {
- String html = parseToHtml("Use the `printf()` function.");
- TestUtil.println("HTML: " + html);
- assertEquals("<p>Use the <code>printf()</code> function.</p>\n", html);
+ String markup = "Use the `printf()` function.";
+ String expectedHtml = "<p>Use the <code>printf()</code> function.</p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -109,9 +269,9 @@ public class MarkdownLanguageSpanElementsTest extends MarkdownLanguageTestBase {
* closing delimiters.
*/
public void testLiteralBacktickInCodeSpan() {
- String html = parseToHtml("``There is a literal backtick (`) here.``");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><code>There is a literal backtick (`) here.</code></p>\n", html);
+ String markup = "``There is a literal backtick (`) here.``";
+ String expectedHtml = "<p><code>There is a literal backtick (`) here.</code></p>\n";
+ parseAndAssert(markup, expectedHtml);
}
/*
@@ -119,24 +279,22 @@ public class MarkdownLanguageSpanElementsTest extends MarkdownLanguageTestBase {
* closing. This allows you to place literal backtick characters at the beginning or end of a code span:
*/
public void testLiteralBacktickAtBeginnionOrIndOfCodeSpan() {
- String html = parseToHtml("`` `foo` ``");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><code>`foo`</code></p>\n", html);
+ parseAndAssert("`` `foo` ``", "<p><code>`foo`</code></p>\n");
}
/*
* With a code span, ampersands and angle brackets are encoded as HTML entities automatically
*/
public void testCodeSpanEncodesAmpersandsAndAngleBrackets() {
- String html = parseToHtml("`Encode tags <p> and enties &code;`");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><code>Encode tags &lt;p&gt; and enties &code;</code></p>\n", html);
+ String markup = "`Encode tags <p> and enties &code;`";
+ String expectedHtml = "<p><code>Encode tags &lt;p&gt; and enties &code;</code></p>\n";
+ parseAndAssert(markup, expectedHtml);
}
public void testNoProcessingInCodeSpan() {
- String html = parseToHtml("`Preserve *asterisk*.`");
- TestUtil.println("HTML: " + html);
- assertEquals("<p><code>Preserve *asterisk*.</code></p>\n", html);
+ String markup = "`Preserve *asterisk*.`";
+ String expectedHtml = "<p><code>Preserve *asterisk*.</code></p>\n";
+ parseAndAssert(markup, expectedHtml);
}
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java
index cc53ea7..ac310c6 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -36,6 +36,14 @@ public class MarkdownLanguageTest extends MarkdownLanguageTestBase {
text.append("> Blockquote\n");
text.append("\n");
text.append(" Code block\n");
+ text.append("\n");
+ text.append("I get 10 times more traffic from [Google] [1] than from [Yahoo][] or [MSN] [].\n");
+ text.append("\n");
+ text.append(" [1]: http://google.com/ \"Google\"\n");
+ text.append(" [YAHOO]: http://search.yahoo.com/ 'Yahoo Search'\n");
+ text.append(" [msn]: http://search.msn.com/ (MSN Search)\n");
+ text.append("\n");
+ text.append("More text.\n");
String html = parseToHtml(text.toString());
TestUtil.println("HTML: " + html);
@@ -46,5 +54,12 @@ public class MarkdownLanguageTest extends MarkdownLanguageTestBase {
assertTrue(html.contains("<h2>Header 2<"));
assertTrue(html.contains("<blockquote><p>Blockquote"));
assertTrue(html.contains("<pre><code>Code block"));
+ assertTrue(html.contains("<a href=\"http://google.com/\" title=\"Google\">Google</a>"));
+ assertTrue(html.contains("<a href=\"http://search.yahoo.com/\" title=\"Yahoo Search\">Yahoo</a>"));
+ assertTrue(html.contains("<a href=\"http://search.msn.com/\" title=\"MSN Search\">MSN</a>"));
+ assertFalse(html.contains("[1]"));
+ assertFalse(html.contains("[YAHOO]"));
+ assertFalse(html.contains("[msn]"));
+ assertTrue(html.contains("<p>More text.</p>"));
}
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTestBase.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTestBase.java
index 030647c..eb7ec58 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTestBase.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTestBase.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 Stefan Seelmann and others.
+ * Copyright (c) 2012, 2013 Stefan Seelmann and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,6 +18,7 @@ import junit.framework.TestCase;
import org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
import org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder;
import org.eclipse.mylyn.wikitext.markdown.core.MarkdownLanguage;
+import org.eclipse.mylyn.wikitext.tests.TestUtil;
/**
* Test base for markdown language tests.
@@ -42,4 +43,10 @@ public abstract class MarkdownLanguageTestBase extends TestCase {
parser.parse(markup);
return out.toString();
}
+
+ public void parseAndAssert(String markup, String expectedHtml) {
+ String html = parseToHtml(markup);
+ TestUtil.println("HTML: " + html);
+ assertEquals(expectedHtml, html);
+ }
}
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownReferenceValidationRuleTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownReferenceValidationRuleTest.java
new file mode 100644
index 0000000..ef79328
--- /dev/null
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownReferenceValidationRuleTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Stefan Seelmann and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Seelmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.wikitext.markdown.tests;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.eclipse.mylyn.internal.wikitext.markdown.core.validation.MarkdownReferenceValidationRule;
+import org.eclipse.mylyn.wikitext.core.validation.MarkupValidator;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem;
+import org.eclipse.mylyn.wikitext.core.validation.ValidationProblem.Severity;
+import org.eclipse.mylyn.wikitext.tests.TestUtil;
+
+public class MarkdownReferenceValidationRuleTest extends TestCase {
+
+ private MarkdownReferenceValidationRule rule;
+
+ private MarkupValidator validator;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ rule = new MarkdownReferenceValidationRule();
+ validator = new MarkupValidator();
+ validator.getRules().add(rule);
+ }
+
+ public void testNoErrorInLocalReferenceToExistingAnchor() {
+ final String markup = "# Header 1\n\n[Link to title](#Header1)";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertTrue(problems.isEmpty());
+ }
+
+ public void testErrorInLocalReferenceToNonExistingAnchor() {
+ final String markup = "# Header 1\n\n[Link to title](#FooBar)";
+ List<ValidationProblem> problems = rule.findProblems(markup, 0, markup.length());
+ TestUtil.println(problems);
+ assertNotNull(problems);
+ assertEquals(1, problems.size());
+ assertEquals(12, problems.get(0).getOffset());
+ assertEquals(24, problems.get(0).getLength());
+ assertEquals(Severity.ERROR, problems.get(0).getSeverity());
+ assertTrue(problems.get(0).getMessage().contains("FooBar"));
+ }
+
+}