aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Formats/StructuredFormatService.kt6
-rw-r--r--src/Formats/TextFormatService.kt4
-rw-r--r--src/Markdown/GeneratedParserUtilBase.java1031
-rw-r--r--src/Markdown/MarkdownLexer.java10
-rw-r--r--src/Markdown/MarkdownProcessor.kt56
-rw-r--r--src/Markdown/MarkdownTokenType.kt6
-rw-r--r--src/Markdown/_MarkdownLexer.flex40
-rw-r--r--src/Markdown/markdown.bnf83
-rw-r--r--src/Markdown/markdown.leg781
-rw-r--r--src/Model/DocumentationContent.kt74
-rw-r--r--src/Model/DocumentationNodeBuilder.kt2
-rw-r--r--src/Processing/CrossReferences.kt12
-rw-r--r--src/RichContent/RichString.kt3
-rw-r--r--src/main.kt15
14 files changed, 2083 insertions, 40 deletions
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index 339ccf73..0c58f553 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -63,8 +63,10 @@ public abstract class StructuredFormatService(val locationService: LocationServi
}
appendLine(to, formatText(node.doc.description))
appendLine(to)
- for (section in node.doc.sections) {
- appendLine(to, formatBold(formatText(section.label)))
+ for ((label, section) in node.doc.sections) {
+ if (label.startsWith("$"))
+ continue
+ appendLine(to, formatBold(formatText(label)))
appendLine(to, formatText(section.text))
appendLine(to)
}
diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt
index 29f01a74..77a0bb65 100644
--- a/src/Formats/TextFormatService.kt
+++ b/src/Formats/TextFormatService.kt
@@ -12,8 +12,8 @@ public class TextFormatService(val signatureGenerator: LanguageService) : Format
for (n in 0..node.doc.summary.length())
append("=")
- for (section in node.doc.sections) {
- appendln(section.label)
+ for ((label,section) in node.doc.sections) {
+ appendln(label)
appendln(section.text)
}
}
diff --git a/src/Markdown/GeneratedParserUtilBase.java b/src/Markdown/GeneratedParserUtilBase.java
new file mode 100644
index 00000000..9dd999b5
--- /dev/null
+++ b/src/Markdown/GeneratedParserUtilBase.java
@@ -0,0 +1,1031 @@
+/*
+ * Copyright 2011-2014 Gregory Shrago
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.dokka.Markdown;
+
+import com.intellij.lang.*;
+import com.intellij.lang.impl.PsiBuilderAdapter;
+import com.intellij.lang.impl.PsiBuilderImpl;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringHash;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.impl.source.resolve.FileContextUtil;
+import com.intellij.psi.impl.source.tree.CompositePsiElement;
+import com.intellij.psi.tree.ICompositeElementType;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.tree.TokenSet;
+import com.intellij.util.Function;
+import com.intellij.util.PairProcessor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.LimitedPool;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * @author gregsh
+ */
+public class GeneratedParserUtilBase {
+
+ private static final Logger LOG = Logger.getInstance("org.intellij.grammar.parser.GeneratedParserUtilBase");
+
+ private static final int MAX_RECURSION_LEVEL = 1000;
+ private static final int MAX_VARIANTS_SIZE = 10000;
+ private static final int MAX_VARIANTS_TO_DISPLAY = 50;
+
+ private static final int INITIAL_VARIANTS_SIZE = 1000;
+ private static final int VARIANTS_POOL_SIZE = 10000;
+ private static final int FRAMES_POOL_SIZE = 500;
+
+ public static final IElementType DUMMY_BLOCK = new DummyBlockElementType();
+
+ public interface Parser {
+ boolean parse(PsiBuilder builder, int level);
+ }
+
+ public static final Parser TOKEN_ADVANCER = new Parser() {
+ @Override
+ public boolean parse(PsiBuilder builder, int level) {
+ if (builder.eof()) return false;
+ builder.advanceLexer();
+ return true;
+ }
+ };
+
+ public static final Parser TRUE_CONDITION = new Parser() {
+ @Override
+ public boolean parse(PsiBuilder builder, int level) {
+ return true;
+ }
+ };
+
+ public static boolean eof(PsiBuilder builder_, int level_) {
+ return builder_.eof();
+ }
+
+ public static int current_position_(PsiBuilder builder_) {
+ return builder_.rawTokenIndex();
+ }
+
+ public static boolean recursion_guard_(PsiBuilder builder_, int level_, String funcName_) {
+ if (level_ > MAX_RECURSION_LEVEL) {
+ builder_.error("Maximum recursion level (" + MAX_RECURSION_LEVEL + ") reached in '" + funcName_ + "'");
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean empty_element_parsed_guard_(PsiBuilder builder_, String funcName_, int prev_position_) {
+ if (prev_position_ == current_position_(builder_)) {
+ builder_.error("Empty element parsed in '" + funcName_ + "' at offset " + builder_.getCurrentOffset());
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean invalid_left_marker_guard_(PsiBuilder builder_, PsiBuilder.Marker marker_, String funcName_) {
+ //builder_.error("Invalid left marker encountered in " + funcName_ +" at offset " + builder_.getCurrentOffset());
+ boolean goodMarker = marker_ != null; // && ((LighterASTNode)marker_).getTokenType() != TokenType.ERROR_ELEMENT;
+ if (!goodMarker) return false;
+ ErrorState state = ErrorState.get(builder_);
+
+ return !state.frameStack.isEmpty();
+ }
+
+ public static TokenSet create_token_set_(IElementType... tokenTypes_) {
+ return TokenSet.create(tokenTypes_);
+ }
+
+ private static boolean consumeTokens(PsiBuilder builder_, boolean smart, int pin, IElementType... tokens) {
+ ErrorState state = ErrorState.get(builder_);
+ if (state.completionState != null && state.predicateCount == 0) {
+ addCompletionVariant(builder_, state.completionState, tokens);
+ }
+ // suppress single token completion
+ CompletionState completionState = state.completionState;
+ state.completionState = null;
+ boolean result_ = true;
+ boolean pinned_ = false;
+ for (int i = 0, tokensLength = tokens.length; i < tokensLength; i++) {
+ if (pin > 0 && i == pin) pinned_ = result_;
+ if (result_ || pinned_) {
+ boolean fast = smart && i == 0;
+ if (!(fast ? consumeTokenFast(builder_, tokens[i]) : consumeToken(builder_, tokens[i]))) {
+ result_ = false;
+ if (pin < 0 || pinned_) report_error_(builder_, state, false);
+ }
+ }
+ }
+ state.completionState = completionState;
+ return pinned_ || result_;
+ }
+
+ public static boolean consumeTokens(PsiBuilder builder_, int pin_, IElementType... token) {
+ return consumeTokens(builder_, false, pin_, token);
+ }
+
+ public static boolean consumeTokensSmart(PsiBuilder builder_, int pin_, IElementType... token) {
+ return consumeTokens(builder_, true, pin_, token);
+ }
+
+ public static boolean parseTokens(PsiBuilder builder_, int pin_, IElementType... tokens) {
+ return parseTokens(builder_, false, pin_, tokens);
+ }
+
+ public static boolean parseTokensSmart(PsiBuilder builder_, int pin_, IElementType... tokens) {
+ return parseTokens(builder_, true, pin_, tokens);
+ }
+
+ public static boolean parseTokens(PsiBuilder builder_, boolean smart, int pin_, IElementType... tokens) {
+ PsiBuilder.Marker marker_ = builder_.mark();
+ boolean result_ = consumeTokens(builder_, smart, pin_, tokens);
+ if (!result_) {
+ marker_.rollbackTo();
+ }
+ else {
+ marker_.drop();
+ }
+ return result_;
+ }
+
+ public static boolean consumeTokenSmart(PsiBuilder builder_, IElementType token) {
+ addCompletionVariantSmart(builder_, token);
+ return consumeTokenFast(builder_, token);
+ }
+
+ public static boolean consumeTokenSmart(PsiBuilder builder_, String token) {
+ addCompletionVariantSmart(builder_, token);
+ return consumeTokenFast(builder_, token);
+ }
+
+ public static boolean consumeToken(PsiBuilder builder_, IElementType token) {
+ addVariantSmart(builder_, token, true);
+ if (nextTokenIsFast(builder_, token)) {
+ builder_.advanceLexer();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean consumeTokenFast(PsiBuilder builder_, IElementType token) {
+ if (nextTokenIsFast(builder_, token)) {
+ builder_.advanceLexer();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean consumeToken(PsiBuilder builder_, String text) {
+ return consumeToken(builder_, text, ErrorState.get(builder_).caseSensitive);
+ }
+
+ public static boolean consumeToken(PsiBuilder builder_, String text, boolean caseSensitive) {
+ addVariantSmart(builder_, text, true);
+ int count = nextTokenIsFast(builder_, text, caseSensitive);
+ if (count > 0) {
+ while (count-- > 0) builder_.advanceLexer();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean consumeTokenFast(PsiBuilder builder_, String text) {
+ int count = nextTokenIsFast(builder_, text, ErrorState.get(builder_).caseSensitive);
+ if (count > 0) {
+ while (count-- > 0) builder_.advanceLexer();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean nextTokenIsFast(PsiBuilder builder_, IElementType token) {
+ return builder_.getTokenType() == token;
+ }
+
+ public static boolean nextTokenIsFast(PsiBuilder builder_, IElementType... tokens) {
+ IElementType tokenType = builder_.getTokenType();
+ for (IElementType token : tokens) {
+ if (token == tokenType) return true;
+ }
+ return false;
+ }
+
+ public static boolean nextTokenIs(PsiBuilder builder_, String frameName, IElementType... tokens) {
+ ErrorState state = ErrorState.get(builder_);
+ if (state.completionState != null) return true;
+ boolean track = !state.suppressErrors && state.predicateCount < 2 && state.predicateSign;
+ if (!track) return nextTokenIsFast(builder_, tokens);
+ IElementType tokenType = builder_.getTokenType();
+ if (StringUtil.isNotEmpty(frameName)) {
+ addVariantInner(state, builder_.rawTokenIndex(), frameName);
+ }
+ else {
+ for (IElementType token : tokens) {
+ addVariant(builder_, state, token);
+ }
+ }
+ if (tokenType == null) return false;
+ for (IElementType token : tokens) {
+ if (tokenType == token) return true;
+ }
+ return false;
+ }
+
+ public static boolean nextTokenIs(PsiBuilder builder_, IElementType token) {
+ if (!addVariantSmart(builder_, token, false)) return true;
+ return nextTokenIsFast(builder_, token);
+ }
+
+ public static boolean nextTokenIs(PsiBuilder builder_, String tokenText) {
+ if (!addVariantSmart(builder_, tokenText, false)) return true;
+ return nextTokenIsFast(builder_, tokenText, ErrorState.get(builder_).caseSensitive) > 0;
+ }
+
+ public static boolean nextTokenIsFast(PsiBuilder builder_, String tokenText) {
+ return nextTokenIsFast(builder_, tokenText, ErrorState.get(builder_).caseSensitive) > 0;
+ }
+
+ public static int nextTokenIsFast(PsiBuilder builder_, String tokenText, boolean caseSensitive) {
+ CharSequence sequence = builder_.getOriginalText();
+ int offset = builder_.getCurrentOffset();
+ int endOffset = offset + tokenText.length();
+ CharSequence subSequence = sequence.subSequence(offset, Math.min(endOffset, sequence.length()));
+
+ if (!Comparing.equal(subSequence, tokenText, caseSensitive)) return 0;
+
+ int count = 0;
+ while (true) {
+ int nextOffset = builder_.rawTokenTypeStart(++count);
+ if (nextOffset > endOffset) {
+ return -count;
+ }
+ else if (nextOffset == endOffset) {
+ break;
+ }
+ }
+ return count;
+ }
+
+ private static void addCompletionVariantSmart(PsiBuilder builder_, Object token) {
+ ErrorState state = ErrorState.get(builder_);
+ CompletionState completionState = state.completionState;
+ if (completionState != null && state.predicateCount == 0) {
+ addCompletionVariant(builder_, completionState, token);
+ }
+ }
+
+ private static boolean addVariantSmart(PsiBuilder builder_, Object token, boolean force) {
+ ErrorState state = ErrorState.get(builder_);
+ // skip FIRST check in completion mode
+ if (state.completionState != null && !force) return false;
+ builder_.eof();
+ if (!state.suppressErrors && state.predicateCount < 2) {
+ addVariant(builder_, state, token);
+ }
+ return true;
+ }
+
+ public static void addVariant(PsiBuilder builder_, String text) {
+ addVariant(builder_, ErrorState.get(builder_), text);
+ }
+
+ private static void addVariant(PsiBuilder builder_, ErrorState state, Object o) {
+ builder_.eof(); // skip whitespaces
+ addVariantInner(state, builder_.rawTokenIndex(), o);
+
+ CompletionState completionState = state.completionState;
+ if (completionState != null && state.predicateSign) {
+ addCompletionVariant(builder_, completionState, o);
+ }
+ }
+
+ private static void addVariantInner(ErrorState state, int pos, Object o) {
+ Variant variant = state.VARIANTS.alloc().init(pos, o);
+ if (state.predicateSign) {
+ state.variants.add(variant);
+ if (state.lastExpectedVariantPos < variant.position) {
+ state.lastExpectedVariantPos = variant.position;
+ }
+ }
+ else {
+ state.unexpected.add(variant);
+ }
+ }
+
+ private static void addCompletionVariant(@NotNull PsiBuilder builder_, @NotNull CompletionState completionState, Object o) {
+ int offset = builder_.getCurrentOffset();
+ if (!builder_.eof() && offset == builder_.rawTokenTypeStart(1)) return; // suppress for zero-length tokens
+
+ boolean add = false;
+ int diff = completionState.offset - offset;
+ String text = completionState.convertItem(o);
+ int length = text == null? 0 : text.length();
+ if (length == 0) return;
+ if (diff == 0) {
+ add = true;
+ }
+ else if (diff > 0 && diff <= length) {
+ CharSequence fragment = builder_.getOriginalText().subSequence(offset, completionState.offset);
+ add = completionState.prefixMatches(fragment.toString(), text);
+ }
+ else if (diff < 0) {
+ for (int i=-1; ; i--) {
+ IElementType type = builder_.rawLookup(i);
+ int tokenStart = builder_.rawTokenTypeStart(i);
+ if (isWhitespaceOrComment(builder_, type)) {
+ diff = completionState.offset - tokenStart;
+ }
+ else if (type != null && tokenStart < completionState.offset) {
+ CharSequence fragment = builder_.getOriginalText().subSequence(tokenStart, completionState.offset);
+ if (completionState.prefixMatches(fragment.toString(), text)) {
+ diff = completionState.offset - tokenStart;
+ }
+ break;
+ }
+ else break;
+ }
+ add = diff >= 0 && diff < length;
+ }
+ add = add && length > 1 && !(text.charAt(0) == '<' && text.charAt(length - 1) == '>') &&
+ !(text.charAt(0) == '\'' && text.charAt(length - 1) == '\'' && length < 5);
+ if (add) {
+ completionState.addItem(builder_, text);
+ }
+ }
+
+ public static boolean isWhitespaceOrComment(@NotNull PsiBuilder builder_, @Nullable IElementType type) {
+ return ((PsiBuilderImpl)((Builder)builder_).getDelegate()).whitespaceOrComment(type);
+ }
+
+ // here's the new section API for compact parsers & less IntelliJ platform API exposure
+ public static final int _NONE_ = 0x0;
+ public static final int _COLLAPSE_ = 0x1;
+ public static final int _LEFT_ = 0x2;
+ public static final int _LEFT_INNER_ = 0x4;
+ public static final int _AND_ = 0x8;
+ public static final int _NOT_ = 0x10;
+
+ // simple enter/exit methods pair that doesn't require frame object
+ public static PsiBuilder.Marker enter_section_(PsiBuilder builder_) {
+ return builder_.mark();
+ }
+
+ public static void exit_section_(PsiBuilder builder_,
+ PsiBuilder.Marker marker,
+ @Nullable IElementType elementType,
+ boolean result) {
+ close_marker_impl_(ErrorState.get(builder_).frameStack.peekLast(), marker, elementType, result);
+ }
+
+ // complex enter/exit methods pair with frame object
+ public static PsiBuilder.Marker enter_section_(PsiBuilder builder_, int level, int modifiers, @Nullable String frameName) {
+ PsiBuilder.Marker marker = builder_.mark();
+ enter_section_impl_(builder_, level, modifiers, frameName);
+ return marker;
+ }
+
+ private static void enter_section_impl_(PsiBuilder builder_, int level, int modifiers, @Nullable String frameName) {
+ ErrorState state = ErrorState.get(builder_);
+ Frame frame = state.FRAMES.alloc().init(builder_, state, level, modifiers, frameName);
+ Frame prevFrame = state.frameStack.peekLast();
+ if (prevFrame != null && prevFrame.errorReportedAt > frame.position) {
+ // report error for previous unsuccessful frame
+ reportError(builder_, state, frame, true, false);
+ }
+ if (((frame.modifiers & _LEFT_) | (frame.modifiers & _LEFT_INNER_)) != 0) {
+ PsiBuilder.Marker left = (PsiBuilder.Marker)builder_.getLatestDoneMarker();
+ if (invalid_left_marker_guard_(builder_, left, frameName)) {
+ frame.leftMarker = left;
+ }
+ }
+ state.frameStack.add(frame);
+ if ((modifiers & _AND_) != 0) {
+ if (state.predicateCount == 0 && !state.predicateSign) {
+ throw new AssertionError("Incorrect false predicate sign");
+ }
+ state.predicateCount++;
+ }
+ else if ((modifiers & _NOT_) != 0) {
+ if (state.predicateCount == 0) {
+ state.predicateSign = false;
+ }
+ else {
+ state.predicateSign = !state.predicateSign;
+ }
+ state.predicateCount++;
+ }
+ }
+
+ public static void exit_section_(PsiBuilder builder_,
+ int level,
+ PsiBuilder.Marker marker,
+ @Nullable IElementType elementType,
+ boolean result,
+ boolean pinned,
+ @Nullable Parser eatMore) {
+ ErrorState state = ErrorState.get(builder_);
+
+ Frame frame = state.frameStack.pollLast();
+ if (frame == null || level != frame.level) {
+ LOG.error("Unbalanced error section: got " + frame + ", expected level " + level);
+ if (frame != null) state.FRAMES.recycle(frame);
+ close_marker_impl_(frame, marker, elementType, result);
+ return;
+ }
+
+ if (((frame.modifiers & _AND_) | (frame.modifiers & _NOT_)) != 0) {
+ close_marker_impl_(frame, marker, null, false);
+ state.predicateCount--;
+ if ((frame.modifiers & _NOT_) != 0) state.predicateSign = !state.predicateSign;
+ state.FRAMES.recycle(frame);
+ return;
+ }
+ exit_section_impl_(state, frame, builder_, marker, elementType, result, pinned);
+
+ int initialPos = builder_.rawTokenIndex();
+ boolean willFail = !result && !pinned;
+ if (willFail && initialPos == frame.position && state.lastExpectedVariantPos == frame.position &&
+ frame.name != null && state.variants.size() - frame.variantCount > 1) {
+ state.clearVariants(true, frame.variantCount);
+ addVariantInner(state, initialPos, frame.name);
+ }
+ int lastErrorPos = getLastVariantPos(state, initialPos);
+ if (!state.suppressErrors && eatMore != null) {
+ state.suppressErrors = true;
+ final boolean eatMoreFlagOnce = !builder_.eof() && eatMore.parse(builder_, frame.level + 1);
+ boolean eatMoreFlag = eatMoreFlagOnce || !result && frame.position == initialPos && lastErrorPos > frame.position;
+
+ PsiBuilderImpl.ProductionMarker latestDoneMarker =
+ (pinned || result) && (state.altMode || elementType != null) &&
+ eatMoreFlagOnce ? (PsiBuilderImpl.ProductionMarker)builder_.getLatestDoneMarker() : null;
+ PsiBuilder.Marker extensionMarker = null;
+ IElementType extensionTokenType = null;
+ // whitespace prefix makes the very first frame offset bigger than marker start offset which is always 0
+ if (latestDoneMarker instanceof PsiBuilder.Marker &&
+ frame.position >= latestDoneMarker.getStartIndex() &&
+ frame.position <= latestDoneMarker.getEndIndex()) {
+ extensionMarker = ((PsiBuilder.Marker)latestDoneMarker).precede();
+ extensionTokenType = latestDoneMarker.getTokenType();
+ ((PsiBuilder.Marker)latestDoneMarker).drop();
+ }
+ // advance to the last error pos
+ // skip tokens until lastErrorPos. parseAsTree might look better here...
+ int parenCount = 0;
+ while ((eatMoreFlag || parenCount > 0) && builder_.rawTokenIndex() < lastErrorPos) {
+ builder_.advanceLexer();
+ eatMoreFlag = eatMore.parse(builder_, frame.level + 1);
+ }
+ boolean errorReported = frame.errorReportedAt == initialPos || !result && frame.errorReportedAt >= frame.position;
+ if (errorReported) {
+ if (eatMoreFlag) {
+ builder_.advanceLexer();
+ parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore);
+ }
+ }
+ else if (eatMoreFlag) {
+ errorReported = reportError(builder_, state, frame, true, true);
+ parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore);
+ }
+ else if (eatMoreFlagOnce || (!result && frame.position != builder_.rawTokenIndex()) || frame.errorReportedAt > initialPos) {
+ errorReported = reportError(builder_, state, frame, true, false);
+ }
+ if (extensionMarker != null) {
+ extensionMarker.done(extensionTokenType);
+ }
+ state.suppressErrors = false;
+ if (errorReported || result) {
+ state.clearVariants(true, 0);
+ state.clearVariants(false, 0);
+ state.lastExpectedVariantPos = -1;
+ }
+ }
+ else if (!result && pinned && frame.errorReportedAt < 0) {
+ // do not report if there are errors beyond current position
+ if (lastErrorPos == initialPos) {
+ // do not force, inner recoverRoot might have skipped some tokens
+ reportError(builder_, state, frame, false, false);
+ }
+ else if (lastErrorPos > initialPos) {
+ // set error pos here as if it is reported for future reference
+ frame.errorReportedAt = lastErrorPos;
+ }
+ }
+ // propagate errorReportedAt up the stack to avoid duplicate reporting
+ Frame prevFrame = willFail && eatMore == null ? null : state.frameStack.peekLast();
+ if (prevFrame != null && prevFrame.errorReportedAt < frame.errorReportedAt) {
+ prevFrame.errorReportedAt = frame.errorReportedAt;
+ }
+ state.FRAMES.recycle(frame);
+ }
+
+ private static void exit_section_impl_(ErrorState state,
+ Frame frame,
+ PsiBuilder builder_,
+ PsiBuilder.Marker marker,
+ IElementType elementType,
+ boolean result,
+ boolean pinned) {
+ if (elementType != null && marker != null) {
+ if ((frame.modifiers & _COLLAPSE_) != 0) {
+ PsiBuilderImpl.ProductionMarker last = result || pinned? (PsiBuilderImpl.ProductionMarker)builder_.getLatestDoneMarker() : null;
+ if (last != null && last.getStartIndex() == frame.position &&
+ state.typeExtends(last.getTokenType(), elementType)) {
+ IElementType resultType = last.getTokenType();
+ ((PsiBuilder.Marker)last).drop();
+ marker.done(resultType);
+ return;
+ }
+ }
+ if (result || pinned) {
+ if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) {
+ marker.done(elementType);
+ frame.leftMarker.precede().done(((LighterASTNode)frame.leftMarker).getTokenType());
+ frame.leftMarker.drop();
+ }
+ else if ((frame.modifiers & _LEFT_) != 0 && frame.leftMarker != null) {
+ marker.drop();
+ frame.leftMarker.precede().done(elementType);
+ }
+ else {
+ if (frame.level == 0) builder_.eof(); // skip whitespaces
+ marker.done(elementType);
+ }
+ }
+ else {
+ close_marker_impl_(frame, marker, null, false);
+ }
+ }
+ else if (result || pinned) {
+ if (marker != null) marker.drop();
+ if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) {
+ frame.leftMarker.precede().done(((LighterASTNode)frame.leftMarker).getTokenType());
+ frame.leftMarker.drop();
+ }
+ }
+ else {
+ close_marker_impl_(frame, marker, null, false);
+ }
+ }
+
+ private static void close_marker_impl_(Frame frame, PsiBuilder.Marker marker, IElementType elementType, boolean result) {
+ if (marker == null) return;
+ if (result) {
+ if (elementType != null) {
+ marker.done(elementType);
+ }
+ else {
+ marker.drop();
+ }
+ }
+ else {
+ if (frame != null) {
+ int position = ((PsiBuilderImpl.ProductionMarker)marker).getStartIndex();
+ if (frame.errorReportedAt > position) {
+ frame.errorReportedAt = frame.errorReportedAtPrev;
+ }
+ }
+ marker.rollbackTo();
+ }
+ }
+
+ public static boolean report_error_(PsiBuilder builder_, boolean result_) {
+ if (!result_) report_error_(builder_, ErrorState.get(builder_), false);
+ return result_;
+ }
+
+ public static void report_error_(PsiBuilder builder_, ErrorState state, boolean advance) {
+ Frame frame = state.frameStack.isEmpty()? null : state.frameStack.getLast();
+ if (frame == null) {
+ LOG.error("unbalanced enter/exit section call: got null");
+ return;
+ }
+ int position = builder_.rawTokenIndex();
+ if (frame.errorReportedAt < position && getLastVariantPos(state, position + 1) <= position) {
+ reportError(builder_, state, frame, true, advance);
+ }
+ }
+
+ private static int getLastVariantPos(ErrorState state, int defValue) {
+ return state.lastExpectedVariantPos < 0? defValue : state.lastExpectedVariantPos;
+ }
+
+ private static boolean reportError(PsiBuilder builder_,
+ ErrorState state,
+ Frame frame,
+ boolean force,
+ boolean advance) {
+ String expectedText = state.getExpectedText(builder_);
+ boolean notEmpty = StringUtil.isNotEmpty(expectedText);
+ if (force || notEmpty || advance) {
+ String gotText = builder_.eof()? "unexpected end of file" :
+ notEmpty? "got '" + builder_.getTokenText() +"'" :
+ "'" + builder_.getTokenText() +"' unexpected";
+ String message = expectedText + gotText;
+ if (advance) {
+ PsiBuilder.Marker mark = builder_.mark();
+ builder_.advanceLexer();
+ mark.error(message);
+ }
+ else {
+ builder_.error(message);
+ }
+ builder_.eof(); // skip whitespaces
+ frame.errorReportedAt = builder_.rawTokenIndex();
+ return true;
+ }
+ return false;
+ }
+
+
+ public static final Key<CompletionState> COMPLETION_STATE_KEY = Key.create("COMPLETION_STATE_KEY");
+
+ public static class CompletionState implements Function<Object, String> {
+ public final int offset;
+ public final Collection<String> items = ContainerUtil.newTroveSet();
+
+ public CompletionState(int offset_) {
+ offset = offset_;
+ }
+
+ @Nullable
+ public String convertItem(Object o) {
+ return o instanceof Object[] ? StringUtil.join((Object[]) o, this, " ") : o.toString();
+ }
+
+ @Override
+ public String fun(Object o) {
+ return o.toString();
+ }
+
+ public void addItem(@NotNull PsiBuilder builder, @NotNull String text) {
+ items.add(text);
+ }
+
+ public boolean prefixMatches(@NotNull String prefix, @NotNull String variant) {
+ return StringUtil.startsWithIgnoreCase(variant, prefix);
+ }
+ }
+
+ public static class Builder extends PsiBuilderAdapter {
+ public final ErrorState state;
+ public final PsiParser parser;
+
+ public Builder(PsiBuilder builder_, ErrorState state_, PsiParser parser_) {
+ super(builder_);
+ state = state_;
+ parser = parser_;
+ }
+
+ public Lexer getLexer() {
+ return ((PsiBuilderImpl)myDelegate).getLexer();
+ }
+ }
+
+ public static PsiBuilder adapt_builder_(IElementType root, PsiBuilder builder, PsiParser parser) {
+ return adapt_builder_(root, builder, parser, null);
+ }
+
+ public static PsiBuilder adapt_builder_(IElementType root, PsiBuilder builder, PsiParser parser, TokenSet[] extendsSets) {
+ ErrorState state = new ErrorState();
+ ErrorState.initState(state, builder, root, extendsSets);
+ return new Builder(builder, state, parser);
+ }
+
+ public static class ErrorState {
+ TokenSet[] extendsSets;
+ public PairProcessor<IElementType, IElementType> altExtendsChecker;
+
+ int predicateCount;
+ boolean predicateSign = true;
+ boolean suppressErrors;
+ public final LinkedList<Frame> frameStack = new LinkedList<Frame>();
+ public CompletionState completionState;
+
+ private boolean caseSensitive;
+ public boolean altMode;
+
+ int lastExpectedVariantPos = -1;
+ MyList<Variant> variants = new MyList<Variant>(INITIAL_VARIANTS_SIZE);
+ MyList<Variant> unexpected = new MyList<Variant>(INITIAL_VARIANTS_SIZE / 10);
+
+ final LimitedPool<Variant> VARIANTS = new LimitedPool<Variant>(VARIANTS_POOL_SIZE, new LimitedPool.ObjectFactory<Variant>() {
+ @Override
+ public Variant create() {
+ return new Variant();
+ }
+
+ @Override
+ public void cleanup(final Variant o) {
+ }
+ });
+ final LimitedPool<Frame> FRAMES = new LimitedPool<Frame>(FRAMES_POOL_SIZE, new LimitedPool.ObjectFactory<Frame>() {
+ @Override
+ public Frame create() {
+ return new Frame();
+ }
+
+ @Override
+ public void cleanup(final Frame o) {
+ }
+ });
+
+ public static ErrorState get(PsiBuilder builder) {
+ return ((Builder)builder).state;
+ }
+
+ public static void initState(ErrorState state, PsiBuilder builder, IElementType root, TokenSet[] extendsSets) {
+ state.extendsSets = extendsSets;
+ PsiFile file = builder.getUserDataUnprotected(FileContextUtil.CONTAINING_FILE_KEY);
+ state.completionState = file == null? null: file.getUserData(COMPLETION_STATE_KEY);
+ Language language = file == null? root.getLanguage() : file.getLanguage();
+ state.caseSensitive = language.isCaseSensitive();
+ }
+
+ public String getExpectedText(PsiBuilder builder_) {
+ int position = builder_.rawTokenIndex();
+ StringBuilder sb = new StringBuilder();
+ if (addExpected(sb, position, true)) {
+ sb.append(" expected, ");
+ }
+ else if (addExpected(sb, position, false)) sb.append(" unexpected, ");
+ return sb.toString();
+ }
+
+ private boolean addExpected(StringBuilder sb, int position, boolean expected) {
+ MyList<Variant> list = expected ? variants : unexpected;
+ String[] strings = new String[list.size()];
+ long[] hashes = new long[strings.length];
+ Arrays.fill(strings, "");
+ int count = 0;
+ loop: for (Variant variant : list) {
+ if (position == variant.position) {
+ String text = variant.object.toString();
+ long hash = StringHash.calc(text);
+ for (int i=0; i<count; i++) {
+ if (hashes[i] == hash) continue loop;
+ }
+ hashes[count] = hash;
+ strings[count] = text;
+ count++;
+ }
+ }
+ Arrays.sort(strings);
+ count = 0;
+ for (String s : strings) {
+ if (s.length() == 0) continue;
+ if (count++ > 0) {
+ if (count > MAX_VARIANTS_TO_DISPLAY) {
+ sb.append(" and ...");
+ break;
+ }
+ else {
+ sb.append(", ");
+ }
+ }
+ char c = s.charAt(0);
+ String displayText = c == '<' || StringUtil.isJavaIdentifierStart(c) ? s : '\'' + s + '\'';
+ sb.append(displayText);
+ }
+ if (count > 1 && count < MAX_VARIANTS_TO_DISPLAY) {
+ int idx = sb.lastIndexOf(", ");
+ sb.replace(idx, idx + 1, " or");
+ }
+ return count > 0;
+ }
+
+ public void clearVariants(boolean expected, int start) {
+ MyList<Variant> list = expected? variants : unexpected;
+ if (start < 0 || start >= list.size()) return;
+ for (int i = start, len = list.size(); i < len; i ++) {
+ VARIANTS.recycle(list.get(i));
+ }
+ list.setSize(start);
+ }
+
+ boolean typeExtends(IElementType child_, IElementType parent_) {
+ if (child_ == parent_) return true;
+ if (extendsSets != null) {
+ for (TokenSet set : extendsSets) {
+ if (set.contains(child_) && set.contains(parent_)) return true;
+ }
+ }
+ return altExtendsChecker != null && altExtendsChecker.process(child_, parent_);
+ }
+ }
+
+ public static class Frame {
+ public int offset;
+ public int position;
+ public int level;
+ public int modifiers;
+ public String name;
+ public int variantCount;
+ public int errorReportedAt;
+ public int errorReportedAtPrev;
+ public PsiBuilder.Marker leftMarker;
+
+ public Frame() {
+ }
+
+ public Frame init(PsiBuilder builder_, ErrorState state, int level_, int modifiers_, String name_) {
+ offset = builder_.getCurrentOffset();
+ position = builder_.rawTokenIndex();
+ level = level_;
+ modifiers = modifiers_;
+ name = name_;
+ variantCount = state.variants.size();
+ errorReportedAt = -1;
+
+ Frame prev = state.frameStack.peekLast();
+ errorReportedAtPrev = prev == null? -1 : prev.errorReportedAt;
+ leftMarker = null;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ String mod = modifiers == _NONE_ ? "_NONE_, " :
+ ((modifiers & _COLLAPSE_) != 0? "_CAN_COLLAPSE_, ": "") +
+ ((modifiers & _LEFT_) != 0? "_LEFT_, ": "") +
+ ((modifiers & _LEFT_INNER_) != 0? "_LEFT_INNER_, ": "") +
+ ((modifiers & _AND_) != 0? "_AND_, ": "") +
+ ((modifiers & _NOT_) != 0? "_NOT_, ": "");
+ return String.format("{%s:%s:%d, %d, %s%s}", offset, position, level, errorReportedAt, mod, name);
+ }
+ }
+
+
+ private static class Variant {
+ int position;
+ Object object;
+
+ public Variant init(int pos, Object o) {
+ position = pos;
+ object = o;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + position + ", " + object + ">";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Variant variant = (Variant)o;
+
+ if (position != variant.position) return false;
+ if (!this.object.equals(variant.object)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = position;
+ result = 31 * result + object.hashCode();
+ return result;
+ }
+ }
+
+
+ private static final int MAX_CHILDREN_IN_TREE = 10;
+ public static boolean parseAsTree(ErrorState state, final PsiBuilder builder_, int level, final IElementType chunkType,
+ boolean checkBraces, final Parser parser, final Parser eatMoreCondition) {
+ final LinkedList<Pair<PsiBuilder.Marker, PsiBuilder.Marker>> parenList = new LinkedList<Pair<PsiBuilder.Marker, PsiBuilder.Marker>>();
+ final LinkedList<Pair<PsiBuilder.Marker, Integer>> siblingList = new LinkedList<Pair<PsiBuilder.Marker, Integer>>();
+ PsiBuilder.Marker marker = null;
+
+ final Runnable checkSiblingsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ main:
+ while (!siblingList.isEmpty()) {
+ final Pair<PsiBuilder.Marker, PsiBuilder.Marker> parenPair = parenList.peek();
+ final int rating = siblingList.getFirst().second;
+ int count = 0;
+ for (Pair<PsiBuilder.Marker, Integer> pair : siblingList) {
+ if (pair.second != rating || parenPair != null && pair.first == parenPair.second) break main;
+ if (++count >= MAX_CHILDREN_IN_TREE) {
+ final PsiBuilder.Marker parentMarker = pair.first.precede();
+ while (count-- > 0) {
+ siblingList.removeFirst();
+ }
+ parentMarker.done(chunkType);
+ siblingList.addFirst(Pair.create(parentMarker, rating + 1));
+ continue main;
+ }
+ }
+ break;
+ }
+ }
+ };
+ int totalCount = 0;
+ int tokenCount = 0;
+ while (true) {
+ final IElementType tokenType = builder_.getTokenType();
+ if (marker == null) {
+ marker = builder_.mark();
+ }
+ final boolean result = (!parenList.isEmpty() || eatMoreCondition.parse(builder_, level + 1)) && parser.parse(builder_, level + 1);
+ if (result) {
+ tokenCount++;
+ totalCount++;
+ }
+ if (!result) {
+ break;
+ }
+
+ if (tokenCount >= MAX_CHILDREN_IN_TREE && marker != null) {
+ marker.done(chunkType);
+ siblingList.addFirst(Pair.create(marker, 1));
+ checkSiblingsRunnable.run();
+ marker = null;
+ tokenCount = 0;
+ }
+ }
+ if (marker != null) {
+ marker.drop();
+ }
+ for (Pair<PsiBuilder.Marker, PsiBuilder.Marker> pair : parenList) {
+ pair.first.drop();
+ }
+ return totalCount != 0;
+ }
+
+ private static class DummyBlockElementType extends IElementType implements ICompositeElementType{
+ DummyBlockElementType() {
+ super("DUMMY_BLOCK", Language.ANY);
+ }
+
+ @NotNull
+ @Override
+ public ASTNode createCompositeNode() {
+ return new DummyBlock();
+ }
+ }
+
+ public static class DummyBlock extends CompositePsiElement {
+ DummyBlock() {
+ super(DUMMY_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public PsiReference[] getReferences() {
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ @NotNull
+ @Override
+ public Language getLanguage() {
+ return getParent().getLanguage();
+ }
+ }
+
+ private static class MyList<E> extends ArrayList<E> {
+ MyList(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ protected void setSize(int fromIndex) {
+ removeRange(fromIndex, size());
+ }
+
+ @Override
+ public boolean add(E e) {
+ int size = size();
+ if (size >= MAX_VARIANTS_SIZE) {
+ removeRange(MAX_VARIANTS_SIZE / 4, size - MAX_VARIANTS_SIZE / 4);
+ }
+ return super.add(e);
+ }
+ }
+}
diff --git a/src/Markdown/MarkdownLexer.java b/src/Markdown/MarkdownLexer.java
new file mode 100644
index 00000000..3486d795
--- /dev/null
+++ b/src/Markdown/MarkdownLexer.java
@@ -0,0 +1,10 @@
+package org.jetbrains.dokka.Markdown;
+
+import com.intellij.lexer.FlexAdapter;
+import org.jetbrains.markdown.impl._MarkdownLexer;
+
+public class MarkdownLexer extends FlexAdapter {
+ public MarkdownLexer() {
+ super(new _MarkdownLexer());
+ }
+}
diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt
new file mode 100644
index 00000000..9843eb68
--- /dev/null
+++ b/src/Markdown/MarkdownProcessor.kt
@@ -0,0 +1,56 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.markdown.*
+import com.intellij.lang.impl.PsiBuilderImpl
+import com.intellij.psi.tree.TokenSet
+import com.intellij.lang.Language
+import com.intellij.psi.tree.IFileElementType
+import com.intellij.lang.LighterASTNode
+import com.intellij.util.diff.FlyweightCapableTreeStructure
+import com.intellij.openapi.util.Ref
+import org.jetbrains.dokka.Markdown.MarkdownLexer
+
+public class MarkdownProcessor {
+ class object {
+ val EXPR_LANGUAGE = object : Language("MARKDOWN") {}
+ val DOCUMENT = IFileElementType("DOCUMENT", EXPR_LANGUAGE);
+ }
+
+ public fun parse(markdown: String): MarkdownTree {
+ val parser = MarkdownParser()
+ val builder = PsiBuilderImpl(null, null, TokenSet.EMPTY, TokenSet.EMPTY, MarkdownLexer(), null, markdown, null, null)
+ parser.parse_only_(DOCUMENT, builder)
+ val light = builder.getLightTree()!!
+ return MarkdownTree(markdown, light)
+ }
+}
+
+public class MarkdownTree(private val text: String, private val structure: FlyweightCapableTreeStructure<LighterASTNode>) {
+ public fun dump(): String {
+ val sb = StringBuilder()
+ visit(sb, "", structure.getRoot(), structure, text)
+ return sb.toString()
+ }
+}
+
+fun markdownToHtml(markdown : String) : String {
+ return MarkdownProcessor().parse(markdown).dump()
+}
+
+
+fun visit(sb: StringBuilder, indent: String, node: LighterASTNode, structure: FlyweightCapableTreeStructure<LighterASTNode>, markdown: String) {
+ sb.append(indent)
+ sb.append(node.getTokenType().toString())
+ val nodeText = markdown.substring(node.getStartOffset(), node.getEndOffset())
+ sb.append(":" + nodeText.replace("\n","\u23CE"))
+ sb.appendln()
+ val ref = Ref.create<Array<LighterASTNode>?>()
+ val count = structure.getChildren(node, ref)
+ val children = ref.get()
+ if (children == null)
+ return
+ for (index in 0..count - 1) {
+ val child = children[index]
+ visit(sb, indent + " ", child, structure, markdown)
+ }
+} \ No newline at end of file
diff --git a/src/Markdown/MarkdownTokenType.kt b/src/Markdown/MarkdownTokenType.kt
new file mode 100644
index 00000000..293228c3
--- /dev/null
+++ b/src/Markdown/MarkdownTokenType.kt
@@ -0,0 +1,6 @@
+package org.jetbrains.dokka.Markdown
+
+import com.intellij.psi.tree.IElementType
+
+public class MarkdownTokenType(debugName: String) : IElementType(debugName, null) {
+} \ No newline at end of file
diff --git a/src/Markdown/_MarkdownLexer.flex b/src/Markdown/_MarkdownLexer.flex
new file mode 100644
index 00000000..9c76da8f
--- /dev/null
+++ b/src/Markdown/_MarkdownLexer.flex
@@ -0,0 +1,40 @@
+package org.jetbrains.markdown.impl;
+
+import com.intellij.lexer.*;
+import com.intellij.psi.tree.IElementType;
+import static org.jetbrains.markdown.MarkdownElementTypes.*;
+
+%%
+
+%{
+ public _MarkdownLexer() {
+ this((java.io.Reader)null);
+ }
+%}
+
+%public
+%class _MarkdownLexer
+%implements FlexLexer
+%function advance
+%type IElementType
+%unicode
+
+Newline="\r"|"\n"|"\r\n"
+Spacechar=[\ \t\f]
+NUMBER=[0-9]+(\.[0-9]*)?
+STRING=[^~\*_`&\[\]()<!#\\ \t\n\r]+
+ANYCHAR=.
+Line=!'\r' !'\n' .* {Newline}
+
+%%
+<YYINITIAL> {
+ {Spacechar} { return SPACECHAR; }
+ {Newline} { return NEWLINE; }
+ "\\357\\273\\277" { return BOM; }
+
+ {NUMBER} { return NUMBER; }
+ {STRING} { return STRING; }
+ {ANYCHAR} { return ANYCHAR; }
+
+ [^] { return com.intellij.psi.TokenType.BAD_CHARACTER; }
+}
diff --git a/src/Markdown/markdown.bnf b/src/Markdown/markdown.bnf
new file mode 100644
index 00000000..b0a3ede6
--- /dev/null
+++ b/src/Markdown/markdown.bnf
@@ -0,0 +1,83 @@
+{
+ psiPackage = 'org.jetbrains.markdown'
+ psiImplPackage = 'org.jetbrains.markdown.impl'
+
+ parserClass="org.jetbrains.markdown.MarkdownParser"
+ parserUtilClass="org.jetbrains.dokka.Markdown.GeneratedParserUtilBase"
+ elementTypeHolderClass = 'org.jetbrains.markdown.MarkdownElementTypes'
+
+ tokenTypeClass = 'org.jetbrains.dokka.Markdown.MarkdownTokenType'
+
+ tokens=[
+ LINE_WS='regexp:[\ \t\f]'
+ EOL='"\r"|"\n"|"\r\n"'
+ BOM = '\357\273\277'
+ number='regexp:\d+(\.\d*)?'
+ String='regexp:[^~\*_`&\[\]()<!#\\ \t\n\r]+'
+ AnyChar='regexp:.'
+ ]
+}
+
+Document ::= BOM? ( Block )*
+
+OptionalSpace ::= Spacechar*
+RequiredSpace ::= Spacechar+
+NonindentSpace ::= (" " | " " | " ")?
+
+BlankLine ::= OptionalSpace Newline
+
+Whitespace ::= Spacechar | Newline
+EndLine ::= LineBreak | TerminalEndline | NormalEndline
+NormalEndline ::= OptionalSpace Newline !BlankLine
+TerminalEndline ::= OptionalSpace Newline <<eof>>
+LineBreak ::= " " NormalEndline
+Indent ::= "\t" | " "
+
+// ---- BLOCKS ----
+Block ::= BlankLine* (
+ Para
+ | Plain
+ | OrderedList
+ | BulletList
+ )
+
+Para ::= NonindentSpace Inlines (BlankLine+ | TerminalEndline)
+Plain ::= Inlines
+
+HorizontalRule ::= NonindentSpace
+ ( '*' OptionalSpace '*' OptionalSpace '*' (OptionalSpace '*')*
+ | '-' OptionalSpace '-' OptionalSpace '-' (OptionalSpace '-')*
+ | '_' OptionalSpace '_' OptionalSpace '_' (OptionalSpace '_')*)
+ OptionalSpace Newline BlankLine+
+
+Bullet ::= !HorizontalRule NonindentSpace ('+' | '*' | '-') Spacechar+
+Enumerator ::= NonindentSpace number '.' Spacechar+
+
+BulletList ::= &Bullet List
+OrderedList ::= &Enumerator List
+
+List ::= (ListItem BlankLine*)+
+ListItem ::= (Bullet | Enumerator) ListBlock ( ListContinuationBlock )*
+
+ListBlock ::= !BlankLine Plain ( ListBlockLine )*
+ListBlockLine ::= !BlankLine !(Indent? (Bullet | Enumerator)) !HorizontalRule Indent? Plain
+
+ListContinuationBlock ::= BlankLine* (Indent ListBlock)+
+
+
+// ---- INLINES ----
+Inlines ::= (!EndLine Inline | EndLine &Inline )+ EndLine?
+Inline ::= String | EndLine | RequiredSpace | Strong | Emph | Link
+
+Emph ::= EmphStar | EmphUnderscore
+EmphStar ::= '*' !Whitespace (!'*' Inline)+ '*'
+EmphUnderscore ::= '_' !Whitespace (!'_' Inline)+ '_'
+
+Strong ::= StrongStar | StrongUnderscore
+StrongStar ::= '**' !Whitespace (!'**' Inline)+ '**'
+StrongUnderscore ::= '__' !Whitespace (!'__' Inline)+ '__'
+
+Link ::= ReferenceLink
+ReferenceLink ::= ReferenceLinkSingle
+ReferenceLinkSingle ::= '[' Target ']'
+Target ::= String \ No newline at end of file
diff --git a/src/Markdown/markdown.leg b/src/Markdown/markdown.leg
new file mode 100644
index 00000000..ea8bc522
--- /dev/null
+++ b/src/Markdown/markdown.leg
@@ -0,0 +1,781 @@
+%{
+/**********************************************************************
+
+ markdown_parser.leg - markdown parser in C using a PEG grammar.
+ (c) 2008 John MacFarlane (jgm at berkeley dot edu).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License or the MIT
+ license. See LICENSE for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ ***********************************************************************/
+
+#include <stdbool.h>
+#include <assert.h>
+#include "markdown_peg.h"
+#include "utility_functions.h"
+
+
+
+/**********************************************************************
+
+ Definitions for leg parser generator.
+ YY_INPUT is the function the parser calls to get new input.
+ We take all new input from (static) charbuf.
+
+ ***********************************************************************/
+
+
+
+# define YYSTYPE element *
+#ifdef __DEBUG__
+# define YY_DEBUG 1
+#endif
+
+#define YY_INPUT(buf, result, max_size) \
+{ \
+ int yyc; \
+ if (charbuf && *charbuf != '\0') { \
+ yyc= *charbuf++; \
+ } else { \
+ yyc= EOF; \
+ } \
+ result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \
+}
+
+#define YY_RULE(T) T
+
+
+/**********************************************************************
+
+ PEG grammar and parser actions for markdown syntax.
+
+ ***********************************************************************/
+
+%}
+
+Doc = BOM? a:StartList ( Block { a = cons($$, a); } )*
+ { parse_result = reverse(a); }
+
+Block = BlankLine*
+ ( BlockQuote
+ | Verbatim
+ | Note
+ | Reference
+ | HorizontalRule
+ | Heading
+ | OrderedList
+ | BulletList
+ | HtmlBlock
+ | StyleBlock
+ | Para
+ | Plain )
+
+Para = NonindentSpace a:Inlines BlankLine+
+ { $$ = a; $$->key = PARA; }
+
+Plain = a:Inlines
+ { $$ = a; $$->key = PLAIN; }
+
+AtxInline = !Newline !(Sp '#'* Sp Newline) Inline
+
+AtxStart = < ( "######" | "#####" | "####" | "###" | "##" | "#" ) >
+ { $$ = mk_element(H1 + (strlen(yytext) - 1)); }
+
+AtxHeading = s:AtxStart Sp a:StartList ( AtxInline { a = cons($$, a); } )+ (Sp '#'* Sp)? Newline
+ { $$ = mk_list(s->key, a);
+ free(s); }
+
+SetextHeading = SetextHeading1 | SetextHeading2
+
+SetextBottom1 = '='+ Newline
+
+SetextBottom2 = '-'+ Newline
+
+SetextHeading1 = &(RawLine SetextBottom1)
+ a:StartList ( !Endline Inline { a = cons($$, a); } )+ Sp Newline
+ SetextBottom1 { $$ = mk_list(H1, a); }
+
+SetextHeading2 = &(RawLine SetextBottom2)
+ a:StartList ( !Endline Inline { a = cons($$, a); } )+ Sp Newline
+ SetextBottom2 { $$ = mk_list(H2, a); }
+
+Heading = SetextHeading | AtxHeading
+
+BlockQuote = a:BlockQuoteRaw
+ { $$ = mk_element(BLOCKQUOTE);
+ $$->children = a;
+ }
+
+BlockQuoteRaw = a:StartList
+ (( '>' ' '? Line { a = cons($$, a); } )
+ ( !'>' !BlankLine Line { a = cons($$, a); } )*
+ ( BlankLine { a = cons(mk_str("\n"), a); } )*
+ )+
+ { $$ = mk_str_from_list(a, true);
+ $$->key = RAW;
+ }
+
+NonblankIndentedLine = !BlankLine IndentedLine
+
+VerbatimChunk = a:StartList
+ ( BlankLine { a = cons(mk_str("\n"), a); } )*
+ ( NonblankIndentedLine { a = cons($$, a); } )+
+ { $$ = mk_str_from_list(a, false); }
+
+Verbatim = a:StartList ( VerbatimChunk { a = cons($$, a); } )+
+ { $$ = mk_str_from_list(a, false);
+ $$->key = VERBATIM; }
+
+HorizontalRule = NonindentSpace
+ ( '*' Sp '*' Sp '*' (Sp '*')*
+ | '-' Sp '-' Sp '-' (Sp '-')*
+ | '_' Sp '_' Sp '_' (Sp '_')*)
+ Sp Newline BlankLine+
+ { $$ = mk_element(HRULE); }
+
+Bullet = !HorizontalRule NonindentSpace ('+' | '*' | '-') Spacechar+
+
+BulletList = &Bullet (ListTight | ListLoose)
+ { $$->key = BULLETLIST; }
+
+ListTight = a:StartList
+ ( ListItemTight { a = cons($$, a); } )+
+ BlankLine* !(Bullet | Enumerator)
+ { $$ = mk_list(LIST, a); }
+
+ListLoose = a:StartList
+ ( b:ListItem BlankLine*
+ { element *li;
+ li = b->children;
+ li->contents.str = realloc(li->contents.str, strlen(li->contents.str) + 3);
+ strcat(li->contents.str, "\n\n"); /* In loose list, \n\n added to end of each element */
+ a = cons(b, a);
+ } )+
+ { $$ = mk_list(LIST, a); }
+
+ListItem = ( Bullet | Enumerator )
+ a:StartList
+ ListBlock { a = cons($$, a); }
+ ( ListContinuationBlock { a = cons($$, a); } )*
+ { element *raw;
+ raw = mk_str_from_list(a, false);
+ raw->key = RAW;
+ $$ = mk_element(LISTITEM);
+ $$->children = raw;
+ }
+
+ListItemTight =
+ ( Bullet | Enumerator )
+ a:StartList
+ ListBlock { a = cons($$, a); }
+ ( !BlankLine
+ ListContinuationBlock { a = cons($$, a); } )*
+ !ListContinuationBlock
+ { element *raw;
+ raw = mk_str_from_list(a, false);
+ raw->key = RAW;
+ $$ = mk_element(LISTITEM);
+ $$->children = raw;
+ }
+
+ListBlock = a:StartList
+ !BlankLine Line { a = cons($$, a); }
+ ( ListBlockLine { a = cons($$, a); } )*
+ { $$ = mk_str_from_list(a, false); }
+
+ListContinuationBlock = a:StartList
+ ( < BlankLine* >
+ { if (strlen(yytext) == 0)
+ a = cons(mk_str("\001"), a); /* block separator */
+ else
+ a = cons(mk_str(yytext), a); } )
+ ( Indent ListBlock { a = cons($$, a); } )+
+ { $$ = mk_str_from_list(a, false); }
+
+Enumerator = NonindentSpace [0-9]+ '.' Spacechar+
+
+OrderedList = &Enumerator (ListTight | ListLoose)
+ { $$->key = ORDEREDLIST; }
+
+ListBlockLine = !BlankLine
+ !( Indent? (Bullet | Enumerator) )
+ !HorizontalRule
+ OptionallyIndentedLine
+
+# Parsers for different kinds of block-level HTML content.
+# This is repetitive due to constraints of PEG grammar.
+
+HtmlBlockOpenAddress = '<' Spnl ("address" | "ADDRESS") Spnl HtmlAttribute* '>'
+HtmlBlockCloseAddress = '<' Spnl '/' ("address" | "ADDRESS") Spnl '>'
+HtmlBlockAddress = HtmlBlockOpenAddress (HtmlBlockAddress | !HtmlBlockCloseAddress .)* HtmlBlockCloseAddress
+
+HtmlBlockOpenBlockquote = '<' Spnl ("blockquote" | "BLOCKQUOTE") Spnl HtmlAttribute* '>'
+HtmlBlockCloseBlockquote = '<' Spnl '/' ("blockquote" | "BLOCKQUOTE") Spnl '>'
+HtmlBlockBlockquote = HtmlBlockOpenBlockquote (HtmlBlockBlockquote | !HtmlBlockCloseBlockquote .)* HtmlBlockCloseBlockquote
+
+HtmlBlockOpenCenter = '<' Spnl ("center" | "CENTER") Spnl HtmlAttribute* '>'
+HtmlBlockCloseCenter = '<' Spnl '/' ("center" | "CENTER") Spnl '>'
+HtmlBlockCenter = HtmlBlockOpenCenter (HtmlBlockCenter | !HtmlBlockCloseCenter .)* HtmlBlockCloseCenter
+
+HtmlBlockOpenDir = '<' Spnl ("dir" | "DIR") Spnl HtmlAttribute* '>'
+HtmlBlockCloseDir = '<' Spnl '/' ("dir" | "DIR") Spnl '>'
+HtmlBlockDir = HtmlBlockOpenDir (HtmlBlockDir | !HtmlBlockCloseDir .)* HtmlBlockCloseDir
+
+HtmlBlockOpenDiv = '<' Spnl ("div" | "DIV") Spnl HtmlAttribute* '>'
+HtmlBlockCloseDiv = '<' Spnl '/' ("div" | "DIV") Spnl '>'
+HtmlBlockDiv = HtmlBlockOpenDiv (HtmlBlockDiv | !HtmlBlockCloseDiv .)* HtmlBlockCloseDiv
+
+HtmlBlockOpenDl = '<' Spnl ("dl" | "DL") Spnl HtmlAttribute* '>'
+HtmlBlockCloseDl = '<' Spnl '/' ("dl" | "DL") Spnl '>'
+HtmlBlockDl = HtmlBlockOpenDl (HtmlBlockDl | !HtmlBlockCloseDl .)* HtmlBlockCloseDl
+
+HtmlBlockOpenFieldset = '<' Spnl ("fieldset" | "FIELDSET") Spnl HtmlAttribute* '>'
+HtmlBlockCloseFieldset = '<' Spnl '/' ("fieldset" | "FIELDSET") Spnl '>'
+HtmlBlockFieldset = HtmlBlockOpenFieldset (HtmlBlockFieldset | !HtmlBlockCloseFieldset .)* HtmlBlockCloseFieldset
+
+HtmlBlockOpenForm = '<' Spnl ("form" | "FORM") Spnl HtmlAttribute* '>'
+HtmlBlockCloseForm = '<' Spnl '/' ("form" | "FORM") Spnl '>'
+HtmlBlockForm = HtmlBlockOpenForm (HtmlBlockForm | !HtmlBlockCloseForm .)* HtmlBlockCloseForm
+
+HtmlBlockOpenH1 = '<' Spnl ("h1" | "H1") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH1 = '<' Spnl '/' ("h1" | "H1") Spnl '>'
+HtmlBlockH1 = HtmlBlockOpenH1 (HtmlBlockH1 | !HtmlBlockCloseH1 .)* HtmlBlockCloseH1
+
+HtmlBlockOpenH2 = '<' Spnl ("h2" | "H2") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH2 = '<' Spnl '/' ("h2" | "H2") Spnl '>'
+HtmlBlockH2 = HtmlBlockOpenH2 (HtmlBlockH2 | !HtmlBlockCloseH2 .)* HtmlBlockCloseH2
+
+HtmlBlockOpenH3 = '<' Spnl ("h3" | "H3") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH3 = '<' Spnl '/' ("h3" | "H3") Spnl '>'
+HtmlBlockH3 = HtmlBlockOpenH3 (HtmlBlockH3 | !HtmlBlockCloseH3 .)* HtmlBlockCloseH3
+
+HtmlBlockOpenH4 = '<' Spnl ("h4" | "H4") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH4 = '<' Spnl '/' ("h4" | "H4") Spnl '>'
+HtmlBlockH4 = HtmlBlockOpenH4 (HtmlBlockH4 | !HtmlBlockCloseH4 .)* HtmlBlockCloseH4
+
+HtmlBlockOpenH5 = '<' Spnl ("h5" | "H5") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH5 = '<' Spnl '/' ("h5" | "H5") Spnl '>'
+HtmlBlockH5 = HtmlBlockOpenH5 (HtmlBlockH5 | !HtmlBlockCloseH5 .)* HtmlBlockCloseH5
+
+HtmlBlockOpenH6 = '<' Spnl ("h6" | "H6") Spnl HtmlAttribute* '>'
+HtmlBlockCloseH6 = '<' Spnl '/' ("h6" | "H6") Spnl '>'
+HtmlBlockH6 = HtmlBlockOpenH6 (HtmlBlockH6 | !HtmlBlockCloseH6 .)* HtmlBlockCloseH6
+
+HtmlBlockOpenMenu = '<' Spnl ("menu" | "MENU") Spnl HtmlAttribute* '>'
+HtmlBlockCloseMenu = '<' Spnl '/' ("menu" | "MENU") Spnl '>'
+HtmlBlockMenu = HtmlBlockOpenMenu (HtmlBlockMenu | !HtmlBlockCloseMenu .)* HtmlBlockCloseMenu
+
+HtmlBlockOpenNoframes = '<' Spnl ("noframes" | "NOFRAMES") Spnl HtmlAttribute* '>'
+HtmlBlockCloseNoframes = '<' Spnl '/' ("noframes" | "NOFRAMES") Spnl '>'
+HtmlBlockNoframes = HtmlBlockOpenNoframes (HtmlBlockNoframes | !HtmlBlockCloseNoframes .)* HtmlBlockCloseNoframes
+
+HtmlBlockOpenNoscript = '<' Spnl ("noscript" | "NOSCRIPT") Spnl HtmlAttribute* '>'
+HtmlBlockCloseNoscript = '<' Spnl '/' ("noscript" | "NOSCRIPT") Spnl '>'
+HtmlBlockNoscript = HtmlBlockOpenNoscript (HtmlBlockNoscript | !HtmlBlockCloseNoscript .)* HtmlBlockCloseNoscript
+
+HtmlBlockOpenOl = '<' Spnl ("ol" | "OL") Spnl HtmlAttribute* '>'
+HtmlBlockCloseOl = '<' Spnl '/' ("ol" | "OL") Spnl '>'
+HtmlBlockOl = HtmlBlockOpenOl (HtmlBlockOl | !HtmlBlockCloseOl .)* HtmlBlockCloseOl
+
+HtmlBlockOpenP = '<' Spnl ("p" | "P") Spnl HtmlAttribute* '>'
+HtmlBlockCloseP = '<' Spnl '/' ("p" | "P") Spnl '>'
+HtmlBlockP = HtmlBlockOpenP (HtmlBlockP | !HtmlBlockCloseP .)* HtmlBlockCloseP
+
+HtmlBlockOpenPre = '<' Spnl ("pre" | "PRE") Spnl HtmlAttribute* '>'
+HtmlBlockClosePre = '<' Spnl '/' ("pre" | "PRE") Spnl '>'
+HtmlBlockPre = HtmlBlockOpenPre (HtmlBlockPre | !HtmlBlockClosePre .)* HtmlBlockClosePre
+
+HtmlBlockOpenTable = '<' Spnl ("table" | "TABLE") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTable = '<' Spnl '/' ("table" | "TABLE") Spnl '>'
+HtmlBlockTable = HtmlBlockOpenTable (HtmlBlockTable | !HtmlBlockCloseTable .)* HtmlBlockCloseTable
+
+HtmlBlockOpenUl = '<' Spnl ("ul" | "UL") Spnl HtmlAttribute* '>'
+HtmlBlockCloseUl = '<' Spnl '/' ("ul" | "UL") Spnl '>'
+HtmlBlockUl = HtmlBlockOpenUl (HtmlBlockUl | !HtmlBlockCloseUl .)* HtmlBlockCloseUl
+
+HtmlBlockOpenDd = '<' Spnl ("dd" | "DD") Spnl HtmlAttribute* '>'
+HtmlBlockCloseDd = '<' Spnl '/' ("dd" | "DD") Spnl '>'
+HtmlBlockDd = HtmlBlockOpenDd (HtmlBlockDd | !HtmlBlockCloseDd .)* HtmlBlockCloseDd
+
+HtmlBlockOpenDt = '<' Spnl ("dt" | "DT") Spnl HtmlAttribute* '>'
+HtmlBlockCloseDt = '<' Spnl '/' ("dt" | "DT") Spnl '>'
+HtmlBlockDt = HtmlBlockOpenDt (HtmlBlockDt | !HtmlBlockCloseDt .)* HtmlBlockCloseDt
+
+HtmlBlockOpenFrameset = '<' Spnl ("frameset" | "FRAMESET") Spnl HtmlAttribute* '>'
+HtmlBlockCloseFrameset = '<' Spnl '/' ("frameset" | "FRAMESET") Spnl '>'
+HtmlBlockFrameset = HtmlBlockOpenFrameset (HtmlBlockFrameset | !HtmlBlockCloseFrameset .)* HtmlBlockCloseFrameset
+
+HtmlBlockOpenLi = '<' Spnl ("li" | "LI") Spnl HtmlAttribute* '>'
+HtmlBlockCloseLi = '<' Spnl '/' ("li" | "LI") Spnl '>'
+HtmlBlockLi = HtmlBlockOpenLi (HtmlBlockLi | !HtmlBlockCloseLi .)* HtmlBlockCloseLi
+
+HtmlBlockOpenTbody = '<' Spnl ("tbody" | "TBODY") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTbody = '<' Spnl '/' ("tbody" | "TBODY") Spnl '>'
+HtmlBlockTbody = HtmlBlockOpenTbody (HtmlBlockTbody | !HtmlBlockCloseTbody .)* HtmlBlockCloseTbody
+
+HtmlBlockOpenTd = '<' Spnl ("td" | "TD") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTd = '<' Spnl '/' ("td" | "TD") Spnl '>'
+HtmlBlockTd = HtmlBlockOpenTd (HtmlBlockTd | !HtmlBlockCloseTd .)* HtmlBlockCloseTd
+
+HtmlBlockOpenTfoot = '<' Spnl ("tfoot" | "TFOOT") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTfoot = '<' Spnl '/' ("tfoot" | "TFOOT") Spnl '>'
+HtmlBlockTfoot = HtmlBlockOpenTfoot (HtmlBlockTfoot | !HtmlBlockCloseTfoot .)* HtmlBlockCloseTfoot
+
+HtmlBlockOpenTh = '<' Spnl ("th" | "TH") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTh = '<' Spnl '/' ("th" | "TH") Spnl '>'
+HtmlBlockTh = HtmlBlockOpenTh (HtmlBlockTh | !HtmlBlockCloseTh .)* HtmlBlockCloseTh
+
+HtmlBlockOpenThead = '<' Spnl ("thead" | "THEAD") Spnl HtmlAttribute* '>'
+HtmlBlockCloseThead = '<' Spnl '/' ("thead" | "THEAD") Spnl '>'
+HtmlBlockThead = HtmlBlockOpenThead (HtmlBlockThead | !HtmlBlockCloseThead .)* HtmlBlockCloseThead
+
+HtmlBlockOpenTr = '<' Spnl ("tr" | "TR") Spnl HtmlAttribute* '>'
+HtmlBlockCloseTr = '<' Spnl '/' ("tr" | "TR") Spnl '>'
+HtmlBlockTr = HtmlBlockOpenTr (HtmlBlockTr | !HtmlBlockCloseTr .)* HtmlBlockCloseTr
+
+HtmlBlockOpenScript = '<' Spnl ("script" | "SCRIPT") Spnl HtmlAttribute* '>'
+HtmlBlockCloseScript = '<' Spnl '/' ("script" | "SCRIPT") Spnl '>'
+HtmlBlockScript = HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript
+
+HtmlBlockOpenHead = '<' Spnl ("head" | "HEAD") Spnl HtmlAttribute* '>'
+HtmlBlockCloseHead = '<' Spnl '/' ("head" | "HEAD") Spnl '>'
+HtmlBlockHead = HtmlBlockOpenHead (!HtmlBlockCloseHead .)* HtmlBlockCloseHead
+
+HtmlBlockInTags = HtmlBlockAddress
+ | HtmlBlockBlockquote
+ | HtmlBlockCenter
+ | HtmlBlockDir
+ | HtmlBlockDiv
+ | HtmlBlockDl
+ | HtmlBlockFieldset
+ | HtmlBlockForm
+ | HtmlBlockH1
+ | HtmlBlockH2
+ | HtmlBlockH3
+ | HtmlBlockH4
+ | HtmlBlockH5
+ | HtmlBlockH6
+ | HtmlBlockMenu
+ | HtmlBlockNoframes
+ | HtmlBlockNoscript
+ | HtmlBlockOl
+ | HtmlBlockP
+ | HtmlBlockPre
+ | HtmlBlockTable
+ | HtmlBlockUl
+ | HtmlBlockDd
+ | HtmlBlockDt
+ | HtmlBlockFrameset
+ | HtmlBlockLi
+ | HtmlBlockTbody
+ | HtmlBlockTd
+ | HtmlBlockTfoot
+ | HtmlBlockTh
+ | HtmlBlockThead
+ | HtmlBlockTr
+ | HtmlBlockScript
+ | HtmlBlockHead
+
+HtmlBlock = < ( HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing ) >
+ BlankLine+
+ { if (extension(EXT_FILTER_HTML)) {
+ $$ = mk_list(LIST, NULL);
+ } else {
+ $$ = mk_str(yytext);
+ $$->key = HTMLBLOCK;
+ }
+ }
+
+HtmlBlockSelfClosing = '<' Spnl HtmlBlockType Spnl HtmlAttribute* '/' Spnl '>'
+
+HtmlBlockType = "address" | "blockquote" | "center" | "dir" | "div" | "dl" | "fieldset" | "form" | "h1" | "h2" | "h3" |
+ "h4" | "h5" | "h6" | "hr" | "isindex" | "menu" | "noframes" | "noscript" | "ol" | "p" | "pre" | "table" |
+ "ul" | "dd" | "dt" | "frameset" | "li" | "tbody" | "td" | "tfoot" | "th" | "thead" | "tr" | "script" |
+ "ADDRESS" | "BLOCKQUOTE" | "CENTER" | "DIR" | "DIV" | "DL" | "FIELDSET" | "FORM" | "H1" | "H2" | "H3" |
+ "H4" | "H5" | "H6" | "HR" | "ISINDEX" | "MENU" | "NOFRAMES" | "NOSCRIPT" | "OL" | "P" | "PRE" | "TABLE" |
+ "UL" | "DD" | "DT" | "FRAMESET" | "LI" | "TBODY" | "TD" | "TFOOT" | "TH" | "THEAD" | "TR" | "SCRIPT"
+
+StyleOpen = '<' Spnl ("style" | "STYLE") Spnl HtmlAttribute* '>'
+StyleClose = '<' Spnl '/' ("style" | "STYLE") Spnl '>'
+InStyleTags = StyleOpen (!StyleClose .)* StyleClose
+StyleBlock = < InStyleTags >
+ BlankLine*
+ { if (extension(EXT_FILTER_STYLES)) {
+ $$ = mk_list(LIST, NULL);
+ } else {
+ $$ = mk_str(yytext);
+ $$->key = HTMLBLOCK;
+ }
+ }
+
+Inlines = a:StartList ( !Endline Inline { a = cons($$, a); }
+ | c:Endline &Inline { a = cons(c, a); } )+ Endline?
+ { $$ = mk_list(LIST, a); }
+
+Inline = Str
+ | Endline
+ | UlOrStarLine
+ | Space
+ | Strong
+ | Emph
+ | Strike
+ | Image
+ | Link
+ | NoteReference
+ | InlineNote
+ | Code
+ | RawHtml
+ | Entity
+ | EscapedChar
+ | Smart
+ | Symbol
+
+Space = Spacechar+
+ { $$ = mk_str(" ");
+ $$->key = SPACE; }
+
+Str = a:StartList < NormalChar+ > { a = cons(mk_str(yytext), a); }
+ ( StrChunk { a = cons($$, a); } )*
+ { if (a->next == NULL) { $$ = a; } else { $$ = mk_list(LIST, a); } }
+
+StrChunk = < (NormalChar | '_'+ &Alphanumeric)+ > { $$ = mk_str(yytext); } |
+ AposChunk
+
+AposChunk = &{ extension(EXT_SMART) } '\'' &Alphanumeric
+ { $$ = mk_element(APOSTROPHE); }
+
+EscapedChar = '\\' !Newline < [-\\`|*_{}[\]()#+.!><] >
+ { $$ = mk_str(yytext); }
+
+Entity = ( HexEntity | DecEntity | CharEntity )
+ { $$ = mk_str(yytext); $$->key = HTML; }
+
+Endline = LineBreak | TerminalEndline | NormalEndline
+
+NormalEndline = Sp Newline !BlankLine !'>' !AtxStart
+ !(Line ('='+ | '-'+) Newline)
+ { $$ = mk_str("\n");
+ $$->key = SPACE; }
+
+TerminalEndline = Sp Newline Eof
+ { $$ = NULL; }
+
+LineBreak = " " NormalEndline
+ { $$ = mk_element(LINEBREAK); }
+
+Symbol = < SpecialChar >
+ { $$ = mk_str(yytext); }
+
+# This keeps the parser from getting bogged down on long strings of '*' or '_',
+# or strings of '*' or '_' with space on each side:
+UlOrStarLine = (UlLine | StarLine) { $$ = mk_str(yytext); }
+StarLine = < "****" '*'* > | < Spacechar '*'+ &Spacechar >
+UlLine = < "____" '_'* > | < Spacechar '_'+ &Spacechar >
+
+Emph = EmphStar | EmphUl
+
+Whitespace = Spacechar | Newline
+
+EmphStar = '*' !Whitespace
+ a:StartList
+ ( !'*' b:Inline { a = cons(b, a); }
+ | b:StrongStar { a = cons(b, a); }
+ )+
+ '*'
+ { $$ = mk_list(EMPH, a); }
+
+EmphUl = '_' !Whitespace
+ a:StartList
+ ( !'_' b:Inline { a = cons(b, a); }
+ | b:StrongUl { a = cons(b, a); }
+ )+
+ '_'
+ { $$ = mk_list(EMPH, a); }
+
+Strong = StrongStar | StrongUl
+
+StrongStar = "**" !Whitespace
+ a:StartList
+ ( !"**" b:Inline { a = cons(b, a); })+
+ "**"
+ { $$ = mk_list(STRONG, a); }
+
+StrongUl = "__" !Whitespace
+ a:StartList
+ ( !"__" b:Inline { a = cons(b, a); })+
+ "__"
+ { $$ = mk_list(STRONG, a); }
+
+Strike = &{ extension(EXT_STRIKE) }
+ "~~" !Whitespace
+ a:StartList
+ ( !"~~" b:Inline { a = cons(b, a); })+
+ "~~"
+ { $$ = mk_list(STRIKE, a); }
+
+Image = '!' ( ExplicitLink | ReferenceLink )
+ { if ($$->key == LINK) {
+ $$->key = IMAGE;
+ } else {
+ element *result;
+ result = $$;
+ $$->children = cons(mk_str("!"), result->children);
+ } }
+
+Link = ExplicitLink | ReferenceLink | AutoLink
+
+ReferenceLink = ReferenceLinkDouble | ReferenceLinkSingle
+
+ReferenceLinkDouble = a:Label < Spnl > !"[]" b:Label
+ { link match;
+ if (find_reference(&match, b->children)) {
+ $$ = mk_link(a->children, match.url, match.title);
+ free(a);
+ free_element_list(b);
+ } else {
+ element *result;
+ result = mk_element(LIST);
+ result->children = cons(mk_str("["), cons(a, cons(mk_str("]"), cons(mk_str(yytext),
+ cons(mk_str("["), cons(b, mk_str("]")))))));
+ $$ = result;
+ }
+ }
+
+ReferenceLinkSingle = a:Label < (Spnl "[]")? >
+ { link match;
+ if (find_reference(&match, a->children)) {
+ $$ = mk_link(a->children, match.url, match.title);
+ free(a);
+ }
+ else {
+ element *result;
+ result = mk_element(LIST);
+ result->children = cons(mk_str("["), cons(a, cons(mk_str("]"), mk_str(yytext))));
+ $$ = result;
+ }
+ }
+
+ExplicitLink = l:Label '(' Sp s:Source Spnl t:Title Sp ')'
+ { $$ = mk_link(l->children, s->contents.str, t->contents.str);
+ free_element(s);
+ free_element(t);
+ free(l); }
+
+Source = ( '<' < SourceContents > '>' | < SourceContents > )
+ { $$ = mk_str(yytext); }
+
+SourceContents = ( ( !'(' !')' !'>' Nonspacechar )+ | '(' SourceContents ')')*
+
+Title = ( TitleSingle | TitleDouble | < "" > )
+ { $$ = mk_str(yytext); }
+
+TitleSingle = '\'' < ( !( '\'' Sp ( ')' | Newline ) ) . )* > '\''
+
+TitleDouble = '"' < ( !( '"' Sp ( ')' | Newline ) ) . )* > '"'
+
+AutoLink = AutoLinkUrl | AutoLinkEmail
+
+AutoLinkUrl = '<' < [A-Za-z]+ "://" ( !Newline !'>' . )+ > '>'
+ { $$ = mk_link(mk_str(yytext), yytext, ""); }
+
+AutoLinkEmail = '<' ( "mailto:" )? < [-A-Za-z0-9+_./!%~$]+ '@' ( !Newline !'>' . )+ > '>'
+ { char *mailto = malloc(strlen(yytext) + 8);
+ sprintf(mailto, "mailto:%s", yytext);
+ $$ = mk_link(mk_str(yytext), mailto, "");
+ free(mailto);
+ }
+
+Reference = NonindentSpace !"[]" l:Label ':' Spnl s:RefSrc t:RefTitle BlankLine+
+ { $$ = mk_link(l->children, s->contents.str, t->contents.str);
+ free_element(s);
+ free_element(t);
+ free(l);
+ $$->key = REFERENCE; }
+
+Label = '[' ( !'^' &{ extension(EXT_NOTES) } | &. &{ !extension(EXT_NOTES) } )
+ a:StartList
+ ( !']' Inline { a = cons($$, a); } )*
+ ']'
+ { $$ = mk_list(LIST, a); }
+
+RefSrc = < Nonspacechar+ >
+ { $$ = mk_str(yytext);
+ $$->key = HTML; }
+
+RefTitle = ( RefTitleSingle | RefTitleDouble | RefTitleParens | EmptyTitle )
+ { $$ = mk_str(yytext); }
+
+EmptyTitle = < "" >
+
+RefTitleSingle = Spnl '\'' < ( !( '\'' Sp Newline | Newline ) . )* > '\''
+
+RefTitleDouble = Spnl '"' < ( !('"' Sp Newline | Newline) . )* > '"'
+
+RefTitleParens = Spnl '(' < ( !(')' Sp Newline | Newline) . )* > ')'
+
+References = a:StartList
+ ( b:Reference { a = cons(b, a); } | SkipBlock )*
+ { references = reverse(a); }
+
+Ticks1 = "`" !'`'
+Ticks2 = "``" !'`'
+Ticks3 = "```" !'`'
+Ticks4 = "````" !'`'
+Ticks5 = "`````" !'`'
+
+Code = ( Ticks1 Sp < ( ( !'`' Nonspacechar )+ | !Ticks1 '`'+ | !( Sp Ticks1 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks1
+ | Ticks2 Sp < ( ( !'`' Nonspacechar )+ | !Ticks2 '`'+ | !( Sp Ticks2 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks2
+ | Ticks3 Sp < ( ( !'`' Nonspacechar )+ | !Ticks3 '`'+ | !( Sp Ticks3 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks3
+ | Ticks4 Sp < ( ( !'`' Nonspacechar )+ | !Ticks4 '`'+ | !( Sp Ticks4 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks4
+ | Ticks5 Sp < ( ( !'`' Nonspacechar )+ | !Ticks5 '`'+ | !( Sp Ticks5 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks5
+ )
+ { $$ = mk_str(yytext); $$->key = CODE; }
+
+RawHtml = < (HtmlComment | HtmlBlockScript | HtmlTag) >
+ { if (extension(EXT_FILTER_HTML)) {
+ $$ = mk_list(LIST, NULL);
+ } else {
+ $$ = mk_str(yytext);
+ $$->key = HTML;
+ }
+ }
+
+BlankLine = Sp Newline
+
+Quoted = '"' (!'"' .)* '"' | '\'' (!'\'' .)* '\''
+HtmlAttribute = (AlphanumericAscii | '-')+ Spnl ('=' Spnl (Quoted | (!'>' Nonspacechar)+))? Spnl
+HtmlComment = "<!--" (!"-->" .)* "-->"
+HtmlTag = '<' Spnl '/'? AlphanumericAscii+ Spnl HtmlAttribute* '/'? Spnl '>'
+Eof = !.
+Spacechar = ' ' | '\t'
+Nonspacechar = !Spacechar !Newline .
+Newline = '\n' | '\r' '\n'?
+Sp = Spacechar*
+Spnl = Sp (Newline Sp)?
+SpecialChar = '~' | '*' | '_' | '`' | '&' | '[' | ']' | '(' | ')' | '<' | '!' | '#' | '\\' | '\'' | '"' | ExtendedSpecialChar
+NormalChar = !( SpecialChar | Spacechar | Newline ) .
+Alphanumeric = [0-9A-Za-z] | '\200' | '\201' | '\202' | '\203' | '\204' | '\205' | '\206' | '\207' | '\210' | '\211' | '\212' | '\213' | '\214' | '\215' | '\216' | '\217' | '\220' | '\221' | '\222' | '\223' | '\224' | '\225' | '\226' | '\227' | '\230' | '\231' | '\232' | '\233' | '\234' | '\235' | '\236' | '\237' | '\240' | '\241' | '\242' | '\243' | '\244' | '\245' | '\246' | '\247' | '\250' | '\251' | '\252' | '\253' | '\254' | '\255' | '\256' | '\257' | '\260' | '\261' | '\262' | '\263' | '\264' | '\265' | '\266' | '\267' | '\270' | '\271' | '\272' | '\273' | '\274' | '\275' | '\276' | '\277' | '\300' | '\301' | '\302' | '\303' | '\304' | '\305' | '\306' | '\307' | '\310' | '\311' | '\312' | '\313' | '\314' | '\315' | '\316' | '\317' | '\320' | '\321' | '\322' | '\323' | '\324' | '\325' | '\326' | '\327' | '\330' | '\331' | '\332' | '\333' | '\334' | '\335' | '\336' | '\337' | '\340' | '\341' | '\342' | '\343' | '\344' | '\345' | '\346' | '\347' | '\350' | '\351' | '\352' | '\353' | '\354' | '\355' | '\356' | '\357' | '\360' | '\361' | '\362' | '\363' | '\364' | '\365' | '\366' | '\367' | '\370' | '\371' | '\372' | '\373' | '\374' | '\375' | '\376' | '\377'
+AlphanumericAscii = [A-Za-z0-9]
+Digit = [0-9]
+BOM = "\357\273\277"
+
+HexEntity = < '&' '#' [Xx] [0-9a-fA-F]+ ';' >
+DecEntity = < '&' '#' [0-9]+ > ';' >
+CharEntity = < '&' [A-Za-z0-9]+ ';' >
+
+NonindentSpace = " " | " " | " " | ""
+Indent = "\t" | " "
+IndentedLine = Indent Line
+OptionallyIndentedLine = Indent? Line
+
+# StartList starts a list data structure that can be added to with cons:
+StartList = &.
+ { $$ = NULL; }
+
+Line = RawLine
+ { $$ = mk_str(yytext); }
+RawLine = ( < (!'\r' !'\n' .)* Newline > | < .+ > Eof )
+
+SkipBlock = HtmlBlock
+ | ( !'#' !SetextBottom1 !SetextBottom2 !BlankLine RawLine )+ BlankLine*
+ | BlankLine+
+ | RawLine
+
+# Syntax extensions
+
+ExtendedSpecialChar = &{ extension(EXT_SMART) } ('.' | '-' | '\'' | '"')
+ | &{ extension(EXT_NOTES) } ( '^' )
+
+Smart = &{ extension(EXT_SMART) }
+ ( Ellipsis | Dash | SingleQuoted | DoubleQuoted | Apostrophe )
+
+Apostrophe = '\''
+ { $$ = mk_element(APOSTROPHE); }
+
+Ellipsis = ("..." | ". . .")
+ { $$ = mk_element(ELLIPSIS); }
+
+Dash = EmDash | EnDash
+
+EnDash = '-' &Digit
+ { $$ = mk_element(ENDASH); }
+
+EmDash = ("---" | "--")
+ { $$ = mk_element(EMDASH); }
+
+SingleQuoteStart = '\'' !(Spacechar | Newline)
+
+SingleQuoteEnd = '\'' !Alphanumeric
+
+SingleQuoted = SingleQuoteStart
+ a:StartList
+ ( !SingleQuoteEnd b:Inline { a = cons(b, a); } )+
+ SingleQuoteEnd
+ { $$ = mk_list(SINGLEQUOTED, a); }
+
+DoubleQuoteStart = '"'
+
+DoubleQuoteEnd = '"'
+
+DoubleQuoted = DoubleQuoteStart
+ a:StartList
+ ( !DoubleQuoteEnd b:Inline { a = cons(b, a); } )+
+ DoubleQuoteEnd
+ { $$ = mk_list(DOUBLEQUOTED, a); }
+
+NoteReference = &{ extension(EXT_NOTES) }
+ ref:RawNoteReference
+ { element *match;
+ if (find_note(&match, ref->contents.str)) {
+ $$ = mk_element(NOTE);
+ assert(match->children != NULL);
+ $$->children = match->children;
+ $$->contents.str = 0;
+ } else {
+ char *s;
+ s = malloc(strlen(ref->contents.str) + 4);
+ sprintf(s, "[^%s]", ref->contents.str);
+ $$ = mk_str(s);
+ free(s);
+ }
+ }
+
+RawNoteReference = "[^" < ( !Newline !']' . )+ > ']'
+ { $$ = mk_str(yytext); }
+
+Note = &{ extension(EXT_NOTES) }
+ NonindentSpace ref:RawNoteReference ':' Sp
+ a:StartList
+ ( RawNoteBlock { a = cons($$, a); } )
+ ( &Indent RawNoteBlock { a = cons($$, a); } )*
+ { $$ = mk_list(NOTE, a);
+ $$->contents.str = strdup(ref->contents.str);
+ }
+
+InlineNote = &{ extension(EXT_NOTES) }
+ "^["
+ a:StartList
+ ( !']' Inline { a = cons($$, a); } )+
+ ']'
+ { $$ = mk_list(NOTE, a);
+ $$->contents.str = 0; }
+
+Notes = a:StartList
+ ( b:Note { a = cons(b, a); } | SkipBlock )*
+ { notes = reverse(a); }
+
+RawNoteBlock = a:StartList
+ ( !BlankLine OptionallyIndentedLine { a = cons($$, a); } )+
+ ( < BlankLine* > { a = cons(mk_str(yytext), a); } )
+ { $$ = mk_str_from_list(a, true);
+ $$->key = RAW;
+ }
+
+%%
+
diff --git a/src/Model/DocumentationContent.kt b/src/Model/DocumentationContent.kt
index cebb429b..77e8c764 100644
--- a/src/Model/DocumentationContent.kt
+++ b/src/Model/DocumentationContent.kt
@@ -9,40 +9,38 @@ public class DocumentationContentSection(public val label: String, public val te
}
}
-// TODO: refactor sections to map
-public class DocumentationContent(public val summary: RichString,
- public val description: RichString,
- public val sections: List<DocumentationContentSection>) {
+public class DocumentationContent(public val sections: Map<String, DocumentationContentSection>) {
+
+ public val summary: RichString get() = sections["\$summary"]?.text ?: RichString.empty
+ public val description: RichString get() = sections["\$description"]?.text ?: RichString.empty
override fun equals(other: Any?): Boolean {
if (other !is DocumentationContent)
return false
- if (summary != other.summary)
- return false
if (sections.size != other.sections.size)
return false
- for (index in sections.indices)
- if (sections[index] != other.sections[index])
+ for (keys in sections.keySet())
+ if (sections[keys] != other.sections[keys])
return false
return true
}
override fun hashCode(): Int {
- return summary.hashCode() + sections.map { it.hashCode() }.sum()
+ return sections.map { it.hashCode() }.sum()
}
override fun toString(): String {
if (sections.isEmpty())
- return summary.toString()
- return "$summary | " + sections.joinToString()
+ return "<empty>"
+ return sections.values().joinToString()
}
val isEmpty: Boolean
get() = description.isEmpty() && sections.none()
class object {
- val Empty = DocumentationContent(RichString.empty, RichString.empty, listOf())
+ val Empty = DocumentationContent(mapOf())
}
}
@@ -50,31 +48,33 @@ public class DocumentationContent(public val summary: RichString,
fun BindingContext.getDocumentation(descriptor: DeclarationDescriptor): DocumentationContent {
val docText = getDocumentationElements(descriptor).map { it.extractText() }.join("\n")
val sections = docText.parseSections()
- val (summary, description) = sections.extractSummaryAndDescription()
- return DocumentationContent(summary, description, sections.drop(1))
+ sections.createSummaryAndDescription()
+ return DocumentationContent(sections)
}
-fun List<DocumentationContentSection>.extractSummaryAndDescription() : Pair<RichString, RichString> {
- // TODO: rework to unify
- // if no $summary and $description is present, parse unnamed section and create specific sections
- // otherwise, create empty sections for missing
+fun MutableMap<String, DocumentationContentSection>.createSummaryAndDescription() {
- val summary = firstOrNull { it.label == "\$summary" }
- if (summary != null) {
- val description = firstOrNull { it.label == "\$description" }
- return Pair(summary.text, description?.text ?: RichString.empty)
+ val summary = get("\$summary")
+ val description = get("\$description")
+ if (summary != null && description == null) {
+ return
}
- val description = firstOrNull { it.label == "\$description" }
- if (description != null) {
- return Pair(RichString.empty, description.text)
+ if (summary == null && description != null) {
+ return
}
- val default = firstOrNull { it.label == "" }?.text
- if (default == null)
- return Pair(RichString.empty, RichString.empty)
+ val unnamed = get("")
+ if (unnamed == null) {
+ return
+ }
- return default.splitBy("\n")
+ val split = unnamed.text.splitBy("\n")
+ remove("")
+ if (!split.first.isEmpty())
+ put("\$summary", DocumentationContentSection("\$summary", split.first))
+ if (!split.second.isEmpty())
+ put("\$description", DocumentationContentSection("\$description", split.second))
}
fun String.parseLabel(index: Int): Pair<String, Int> {
@@ -104,8 +104,8 @@ fun String.parseLabel(index: Int): Pair<String, Int> {
return "" to -1
}
-fun String.parseSections(): List<DocumentationContentSection> {
- val sections = arrayListOf<DocumentationContentSection>()
+fun String.parseSections(): MutableMap<String, DocumentationContentSection> {
+ val sections = hashMapOf<String, DocumentationContentSection>()
var currentLabel = ""
var currentSectionStart = 0
var currentIndex = 0
@@ -117,7 +117,7 @@ fun String.parseSections(): List<DocumentationContentSection> {
// section starts, add previous section
val currentContent = substring(currentSectionStart, currentIndex).trim()
val section = DocumentationContentSection(currentLabel, currentContent.toRichString())
- sections.add(section)
+ sections.put(section.label, section)
currentLabel = label
currentIndex = index + 1
@@ -131,12 +131,20 @@ fun String.parseSections(): List<DocumentationContentSection> {
val currentContent = substring(currentSectionStart, currentIndex).trim()
val section = DocumentationContentSection(currentLabel, currentContent.toRichString())
- sections.add(section)
+ sections.put(section.label, section)
return sections
}
fun String.toRichString() : RichString {
val content = RichString()
+ for(index in indices) {
+ val ch = get(index)
+ when {
+ ch == '\\' -> continue
+ ch == '*' && index < length-1 && !get(index + 1).isWhitespace() -> ch
+ }
+ }
+
content.addSlice(this, NormalStyle)
return content
} \ No newline at end of file
diff --git a/src/Model/DocumentationNodeBuilder.kt b/src/Model/DocumentationNodeBuilder.kt
index c8744172..f724c444 100644
--- a/src/Model/DocumentationNodeBuilder.kt
+++ b/src/Model/DocumentationNodeBuilder.kt
@@ -35,7 +35,7 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip
val classifierDescriptor = typeConstructor.getDeclarationDescriptor()
val name = when (classifierDescriptor) {
is Named -> classifierDescriptor.getName().asString()
- else -> "<BAD>"
+ else -> "<anonymous>"
}
val node = DocumentationNode(descriptor, name, DocumentationContent.Empty, DocumentationNode.Kind.Type)
reference(data, node, DocumentationReference.Kind.Detail)
diff --git a/src/Processing/CrossReferences.kt b/src/Processing/CrossReferences.kt
new file mode 100644
index 00000000..9f21da6e
--- /dev/null
+++ b/src/Processing/CrossReferences.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.dokka
+
+public fun DocumentationNode.buildCrossReferences() {
+ for (member in members) {
+ member.buildCrossReferences()
+ member.details(DocumentationNode.Kind.Receiver).forEach { detail ->
+
+
+ }
+ }
+}
+
diff --git a/src/RichContent/RichString.kt b/src/RichContent/RichString.kt
index f09e4715..2110c47f 100644
--- a/src/RichContent/RichString.kt
+++ b/src/RichContent/RichString.kt
@@ -5,7 +5,8 @@ public class RichString {
public val slices: List<RichStringSlice> get() = sliceList
public fun addSlice(slice: RichStringSlice) {
- sliceList.add(slice)
+ if (slice.text.length() > 0)
+ sliceList.add(slice)
}
public fun addSlice(text: String, style: RichStringStyle) {
diff --git a/src/main.kt b/src/main.kt
index 2f2ac93c..a541831d 100644
--- a/src/main.kt
+++ b/src/main.kt
@@ -59,10 +59,23 @@ public fun main(args: Array<String>) {
val timeAnalyse = System.currentTimeMillis() - startAnalyse
println("done in ${timeAnalyse / 1000} secs")
+ print("Processing cross references... ")
+ val startProcessing = System.currentTimeMillis()
+ documentation.buildCrossReferences()
+ val timeProcessing = System.currentTimeMillis() - startProcessing
+ println("done in ${timeProcessing / 1000} secs")
+
val startBuild = System.currentTimeMillis()
val signatureGenerator = KotlinLanguageService()
val locationService = FoldersLocationService(arguments.outputDir)
- val formatter = JekyllFormatService(locationService, signatureGenerator)
+ val templateService = HtmlTemplateService.default("/dokka/styles/style.css")
+ val resolutionService = object : ResolutionService {
+ override fun resolve(text: String): DocumentationNode {
+ return documentation
+ }
+ }
+
+ val formatter = HtmlFormatService(locationService, resolutionService, signatureGenerator, templateService)
val generator = FileGenerator(signatureGenerator, locationService, formatter)
print("Building pages... ")
generator.buildPage(documentation)