Extract content model, make doc model independent from descriptors, parse doccomments with custom parser, some tests failing due to hanging new lines.
diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml
index 4e7c9c0..deb51b1 100644
--- a/.idea/runConfigurations/All_tests.xml
+++ b/.idea/runConfigurations/All_tests.xml
@@ -23,8 +23,14 @@
       <option name="TRANSPORT" value="0" />
       <option name="LOCAL" value="true" />
     </RunnerSettings>
+    <RunnerSettings RunnerId="Debug">
+      <option name="DEBUG_PORT" value="" />
+      <option name="TRANSPORT" value="0" />
+      <option name="LOCAL" value="true" />
+    </RunnerSettings>
     <RunnerSettings RunnerId="Run" />
     <ConfigurationWrapper RunnerId="Debug" />
+    <ConfigurationWrapper RunnerId="Debug" />
     <ConfigurationWrapper RunnerId="Run" />
     <method />
   </configuration>
diff --git a/gen/org/jetbrains/markdown/MarkdownElementTypes.java b/gen/org/jetbrains/markdown/MarkdownElementTypes.java
index 757cc62..8c2d77b 100644
--- a/gen/org/jetbrains/markdown/MarkdownElementTypes.java
+++ b/gen/org/jetbrains/markdown/MarkdownElementTypes.java
@@ -6,7 +6,9 @@
 
 public interface MarkdownElementTypes {
 
+  IElementType ANONYMOUS_SECTION = new IElementType("ANONYMOUS_SECTION", null);
   IElementType BLANK_LINE = new IElementType("BLANK_LINE", null);
+  IElementType BLOCK = new IElementType("BLOCK", null);
   IElementType BULLET = new IElementType("BULLET", null);
   IElementType BULLET_LIST = new IElementType("BULLET_LIST", null);
   IElementType EMPH = new IElementType("EMPH", null);
@@ -14,22 +16,22 @@
   IElementType ENUMERATOR = new IElementType("ENUMERATOR", null);
   IElementType HORIZONTAL_RULE = new IElementType("HORIZONTAL_RULE", null);
   IElementType HREF = new IElementType("HREF", null);
-  IElementType INDENTED_LINE = new IElementType("INDENTED_LINE", null);
+  IElementType INLINE = new IElementType("INLINE", null);
   IElementType LINK = new IElementType("LINK", null);
   IElementType LIST_BLOCK = new IElementType("LIST_BLOCK", null);
   IElementType LIST_BLOCK_LINE = new IElementType("LIST_BLOCK_LINE", null);
   IElementType LIST_CONTINUATION_BLOCK = new IElementType("LIST_CONTINUATION_BLOCK", null);
   IElementType LIST_ITEM = new IElementType("LIST_ITEM", null);
-  IElementType NONBLANK_INDENTED_LINE = new IElementType("NONBLANK_INDENTED_LINE", null);
+  IElementType NAMED_SECTION = new IElementType("NAMED_SECTION", null);
   IElementType ORDERED_LIST = new IElementType("ORDERED_LIST", null);
   IElementType PARA = new IElementType("PARA", null);
   IElementType PLAIN_TEXT = new IElementType("PLAIN_TEXT", null);
+  IElementType SECTION_BODY = new IElementType("SECTION_BODY", null);
+  IElementType SECTION_NAME = new IElementType("SECTION_NAME", null);
   IElementType STRONG = new IElementType("STRONG", null);
   IElementType STRONG_STAR = new IElementType("STRONG_STAR", null);
   IElementType STRONG_UNDERSCORE = new IElementType("STRONG_UNDERSCORE", null);
   IElementType TARGET = new IElementType("TARGET", null);
-  IElementType VERBATIM = new IElementType("VERBATIM", null);
-  IElementType VERBATIM_ITEM = new IElementType("VERBATIM_ITEM", null);
   IElementType WHITESPACE = new IElementType("WHITESPACE", null);
 
   IElementType ANYCHAR = new MarkdownTokenType("AnyChar");
diff --git a/gen/org/jetbrains/markdown/MarkdownParser.java b/gen/org/jetbrains/markdown/MarkdownParser.java
index 90c6998..09c4a0f 100644
--- a/gen/org/jetbrains/markdown/MarkdownParser.java
+++ b/gen/org/jetbrains/markdown/MarkdownParser.java
@@ -22,9 +22,15 @@
     boolean result_;
     builder_ = adapt_builder_(root_, builder_, this, null);
     Marker marker_ = enter_section_(builder_, 0, _COLLAPSE_, null);
-    if (root_ == BLANK_LINE) {
+    if (root_ == ANONYMOUS_SECTION) {
+      result_ = AnonymousSection(builder_, 0);
+    }
+    else if (root_ == BLANK_LINE) {
       result_ = BlankLine(builder_, 0);
     }
+    else if (root_ == BLOCK) {
+      result_ = Block(builder_, 0);
+    }
     else if (root_ == BULLET) {
       result_ = Bullet(builder_, 0);
     }
@@ -46,8 +52,8 @@
     else if (root_ == HREF) {
       result_ = Href(builder_, 0);
     }
-    else if (root_ == INDENTED_LINE) {
-      result_ = IndentedLine(builder_, 0);
+    else if (root_ == INLINE) {
+      result_ = Inline(builder_, 0);
     }
     else if (root_ == LINK) {
       result_ = Link(builder_, 0);
@@ -64,8 +70,8 @@
     else if (root_ == LIST_ITEM) {
       result_ = ListItem(builder_, 0);
     }
-    else if (root_ == NONBLANK_INDENTED_LINE) {
-      result_ = NonblankIndentedLine(builder_, 0);
+    else if (root_ == NAMED_SECTION) {
+      result_ = NamedSection(builder_, 0);
     }
     else if (root_ == ORDERED_LIST) {
       result_ = OrderedList(builder_, 0);
@@ -76,6 +82,12 @@
     else if (root_ == PLAIN_TEXT) {
       result_ = PlainText(builder_, 0);
     }
+    else if (root_ == SECTION_BODY) {
+      result_ = SectionBody(builder_, 0);
+    }
+    else if (root_ == SECTION_NAME) {
+      result_ = SectionName(builder_, 0);
+    }
     else if (root_ == STRONG) {
       result_ = Strong(builder_, 0);
     }
@@ -88,12 +100,6 @@
     else if (root_ == TARGET) {
       result_ = Target(builder_, 0);
     }
-    else if (root_ == VERBATIM) {
-      result_ = Verbatim(builder_, 0);
-    }
-    else if (root_ == VERBATIM_ITEM) {
-      result_ = VerbatimItem(builder_, 0);
-    }
     else if (root_ == WHITESPACE) {
       result_ = Whitespace(builder_, 0);
     }
@@ -108,6 +114,17 @@
   }
 
   /* ********************************************************** */
+  // SectionBody
+  public static boolean AnonymousSection(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "AnonymousSection")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<anonymous section>");
+    result_ = SectionBody(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, ANONYMOUS_SECTION, result_, false, null);
+    return result_;
+  }
+
+  /* ********************************************************** */
   // OptionalSpace Newline
   public static boolean BlankLine(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "BlankLine")) return false;
@@ -122,19 +139,18 @@
 
   /* ********************************************************** */
   // BlankLine* (
-  //           Para
-  //         | Verbatim
-  //         | OrderedList
-  //         | BulletList
-  //         | Inlines
-  //         )
-  static boolean Block(PsiBuilder builder_, int level_) {
+  //               OrderedList
+  //             | BulletList
+  //             | HorizontalRule
+  //             | Para
+  //             )
+  public static boolean Block(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Block")) return false;
     boolean result_;
-    Marker marker_ = enter_section_(builder_);
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<block>");
     result_ = Block_0(builder_, level_ + 1);
     result_ = result_ && Block_1(builder_, level_ + 1);
-    exit_section_(builder_, marker_, null, result_);
+    exit_section_(builder_, level_, marker_, BLOCK, result_, false, null);
     return result_;
   }
 
@@ -150,20 +166,18 @@
     return true;
   }
 
-  // Para
-  //         | Verbatim
-  //         | OrderedList
-  //         | BulletList
-  //         | Inlines
+  // OrderedList
+  //             | BulletList
+  //             | HorizontalRule
+  //             | Para
   private static boolean Block_1(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Block_1")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
-    result_ = Para(builder_, level_ + 1);
-    if (!result_) result_ = Verbatim(builder_, level_ + 1);
-    if (!result_) result_ = OrderedList(builder_, level_ + 1);
+    result_ = OrderedList(builder_, level_ + 1);
     if (!result_) result_ = BulletList(builder_, level_ + 1);
-    if (!result_) result_ = Inlines(builder_, level_ + 1);
+    if (!result_) result_ = HorizontalRule(builder_, level_ + 1);
+    if (!result_) result_ = Para(builder_, level_ + 1);
     exit_section_(builder_, marker_, null, result_);
     return result_;
   }
@@ -243,13 +257,15 @@
   }
 
   /* ********************************************************** */
-  // BOM? ( Block )*
+  // BOM? Whitespace* AnonymousSection? (Whitespace* NamedSection)*
   static boolean Document(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Document")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
     result_ = Document_0(builder_, level_ + 1);
     result_ = result_ && Document_1(builder_, level_ + 1);
+    result_ = result_ && Document_2(builder_, level_ + 1);
+    result_ = result_ && Document_3(builder_, level_ + 1);
     exit_section_(builder_, marker_, null, result_);
     return result_;
   }
@@ -261,28 +277,60 @@
     return true;
   }
 
-  // ( Block )*
+  // Whitespace*
   private static boolean Document_1(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Document_1")) return false;
     int pos_ = current_position_(builder_);
     while (true) {
-      if (!Document_1_0(builder_, level_ + 1)) break;
+      if (!Whitespace(builder_, level_ + 1)) break;
       if (!empty_element_parsed_guard_(builder_, "Document_1", pos_)) break;
       pos_ = current_position_(builder_);
     }
     return true;
   }
 
-  // ( Block )
-  private static boolean Document_1_0(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "Document_1_0")) return false;
+  // AnonymousSection?
+  private static boolean Document_2(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Document_2")) return false;
+    AnonymousSection(builder_, level_ + 1);
+    return true;
+  }
+
+  // (Whitespace* NamedSection)*
+  private static boolean Document_3(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Document_3")) return false;
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!Document_3_0(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "Document_3", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    return true;
+  }
+
+  // Whitespace* NamedSection
+  private static boolean Document_3_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Document_3_0")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
-    result_ = Block(builder_, level_ + 1);
+    result_ = Document_3_0_0(builder_, level_ + 1);
+    result_ = result_ && NamedSection(builder_, level_ + 1);
     exit_section_(builder_, marker_, null, result_);
     return result_;
   }
 
+  // Whitespace*
+  private static boolean Document_3_0_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Document_3_0_0")) return false;
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!Whitespace(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "Document_3_0_0", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    return true;
+  }
+
   /* ********************************************************** */
   // EmphStar | EmphUnderscore
   public static boolean Emph(PsiBuilder builder_, int level_) {
@@ -663,28 +711,16 @@
   }
 
   /* ********************************************************** */
-  // Indent PlainText
-  public static boolean IndentedLine(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "IndentedLine")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<indented line>");
-    result_ = Indent(builder_, level_ + 1);
-    result_ = result_ && PlainText(builder_, level_ + 1);
-    exit_section_(builder_, level_, marker_, INDENTED_LINE, result_, false, null);
-    return result_;
-  }
-
-  /* ********************************************************** */
   // Strong | Emph | Link | PlainText
-  static boolean Inline(PsiBuilder builder_, int level_) {
+  public static boolean Inline(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Inline")) return false;
     boolean result_;
-    Marker marker_ = enter_section_(builder_);
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<inline>");
     result_ = Strong(builder_, level_ + 1);
     if (!result_) result_ = Emph(builder_, level_ + 1);
     if (!result_) result_ = Link(builder_, level_ + 1);
     if (!result_) result_ = PlainText(builder_, level_ + 1);
-    exit_section_(builder_, marker_, null, result_);
+    exit_section_(builder_, level_, marker_, INLINE, result_, false, null);
     return result_;
   }
 
@@ -1052,24 +1088,14 @@
   }
 
   /* ********************************************************** */
-  // !BlankLine IndentedLine
-  public static boolean NonblankIndentedLine(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "NonblankIndentedLine")) return false;
+  // SectionHeader SectionBody
+  public static boolean NamedSection(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "NamedSection")) return false;
     boolean result_;
-    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<nonblank indented line>");
-    result_ = NonblankIndentedLine_0(builder_, level_ + 1);
-    result_ = result_ && IndentedLine(builder_, level_ + 1);
-    exit_section_(builder_, level_, marker_, NONBLANK_INDENTED_LINE, result_, false, null);
-    return result_;
-  }
-
-  // !BlankLine
-  private static boolean NonblankIndentedLine_0(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "NonblankIndentedLine_0")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_, level_, _NOT_, null);
-    result_ = !BlankLine(builder_, level_ + 1);
-    exit_section_(builder_, level_, marker_, null, result_, false, null);
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<named section>");
+    result_ = SectionHeader(builder_, level_ + 1);
+    result_ = result_ && SectionBody(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, NAMED_SECTION, result_, false, null);
     return result_;
   }
 
@@ -1094,22 +1120,21 @@
   }
 
   /* ********************************************************** */
-  // OptionalSpace Newline !BlankLine
+  // Newline !BlankLine
   static boolean NormalEndline(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "NormalEndline")) return false;
-    if (!nextTokenIs(builder_, "", NEWLINE, SPACECHAR)) return false;
+    if (!nextTokenIs(builder_, NEWLINE)) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
-    result_ = OptionalSpace(builder_, level_ + 1);
-    result_ = result_ && consumeToken(builder_, NEWLINE);
-    result_ = result_ && NormalEndline_2(builder_, level_ + 1);
+    result_ = consumeToken(builder_, NEWLINE);
+    result_ = result_ && NormalEndline_1(builder_, level_ + 1);
     exit_section_(builder_, marker_, null, result_);
     return result_;
   }
 
   // !BlankLine
-  private static boolean NormalEndline_2(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "NormalEndline_2")) return false;
+  private static boolean NormalEndline_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "NormalEndline_1")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_, level_, _NOT_, null);
     result_ = !BlankLine(builder_, level_ + 1);
@@ -1153,21 +1178,27 @@
   }
 
   /* ********************************************************** */
-  // NonindentSpace Inlines (BlankLine | TerminalEndline)
+  // Inlines (BlankLine | TerminalEndline)?
   public static boolean Para(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Para")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_, level_, _NONE_, "<para>");
-    result_ = NonindentSpace(builder_, level_ + 1);
-    result_ = result_ && Inlines(builder_, level_ + 1);
-    result_ = result_ && Para_2(builder_, level_ + 1);
+    result_ = Inlines(builder_, level_ + 1);
+    result_ = result_ && Para_1(builder_, level_ + 1);
     exit_section_(builder_, level_, marker_, PARA, result_, false, null);
     return result_;
   }
 
+  // (BlankLine | TerminalEndline)?
+  private static boolean Para_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Para_1")) return false;
+    Para_1_0(builder_, level_ + 1);
+    return true;
+  }
+
   // BlankLine | TerminalEndline
-  private static boolean Para_2(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "Para_2")) return false;
+  private static boolean Para_1_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "Para_1_0")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
     result_ = BlankLine(builder_, level_ + 1);
@@ -1177,7 +1208,7 @@
   }
 
   /* ********************************************************** */
-  // (String | Number | Spacechar)+
+  // (String | Number | Spacechar+)+
   public static boolean PlainText(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "PlainText")) return false;
     boolean result_;
@@ -1193,14 +1224,30 @@
     return result_;
   }
 
-  // String | Number | Spacechar
+  // String | Number | Spacechar+
   private static boolean PlainText_0(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "PlainText_0")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
     result_ = consumeToken(builder_, STRING);
     if (!result_) result_ = consumeToken(builder_, NUMBER);
-    if (!result_) result_ = consumeToken(builder_, SPACECHAR);
+    if (!result_) result_ = PlainText_0_2(builder_, level_ + 1);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  // Spacechar+
+  private static boolean PlainText_0_2(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "PlainText_0_2")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, SPACECHAR);
+    int pos_ = current_position_(builder_);
+    while (result_) {
+      if (!consumeToken(builder_, SPACECHAR)) break;
+      if (!empty_element_parsed_guard_(builder_, "PlainText_0_2", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
     exit_section_(builder_, marker_, null, result_);
     return result_;
   }
@@ -1219,17 +1266,95 @@
   }
 
   /* ********************************************************** */
+  // Block*
+  public static boolean SectionBody(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionBody")) return false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<section body>");
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!Block(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "SectionBody", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    exit_section_(builder_, level_, marker_, SECTION_BODY, true, false, null);
+    return true;
+  }
+
+  /* ********************************************************** */
+  // '$' SectionName OptionalSpace ':' OptionalSpace
+  static boolean SectionHeader(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionHeader")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, "$");
+    result_ = result_ && SectionName(builder_, level_ + 1);
+    result_ = result_ && OptionalSpace(builder_, level_ + 1);
+    result_ = result_ && consumeToken(builder_, ":");
+    result_ = result_ && OptionalSpace(builder_, level_ + 1);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // SectionNameStart | '{' OptionalSpace SectionNameStart (Spacechar+ String)* OptionalSpace '}'
+  public static boolean SectionName(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionName")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<section name>");
+    result_ = SectionNameStart(builder_, level_ + 1);
+    if (!result_) result_ = SectionName_1(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, SECTION_NAME, result_, false, null);
+    return result_;
+  }
+
+  // '{' OptionalSpace SectionNameStart (Spacechar+ String)* OptionalSpace '}'
+  private static boolean SectionName_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionName_1")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, "{");
+    result_ = result_ && OptionalSpace(builder_, level_ + 1);
+    result_ = result_ && SectionNameStart(builder_, level_ + 1);
+    result_ = result_ && SectionName_1_3(builder_, level_ + 1);
+    result_ = result_ && OptionalSpace(builder_, level_ + 1);
+    result_ = result_ && consumeToken(builder_, "}");
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  // (Spacechar+ String)*
+  private static boolean SectionName_1_3(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionName_1_3")) return false;
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!SectionName_1_3_0(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "SectionName_1_3", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    return true;
+  }
+
+  // Spacechar+ String
+  private static boolean SectionName_1_3_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionName_1_3_0")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_);
+    result_ = SectionName_1_3_0_0(builder_, level_ + 1);
+    result_ = result_ && consumeToken(builder_, STRING);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
   // Spacechar+
-  static boolean RequiredSpace(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "RequiredSpace")) return false;
-    if (!nextTokenIs(builder_, SPACECHAR)) return false;
+  private static boolean SectionName_1_3_0_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionName_1_3_0_0")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
     result_ = consumeToken(builder_, SPACECHAR);
     int pos_ = current_position_(builder_);
     while (result_) {
       if (!consumeToken(builder_, SPACECHAR)) break;
-      if (!empty_element_parsed_guard_(builder_, "RequiredSpace", pos_)) break;
+      if (!empty_element_parsed_guard_(builder_, "SectionName_1_3_0_0", pos_)) break;
       pos_ = current_position_(builder_);
     }
     exit_section_(builder_, marker_, null, result_);
@@ -1237,6 +1362,25 @@
   }
 
   /* ********************************************************** */
+  // '$'? String
+  static boolean SectionNameStart(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionNameStart")) return false;
+    boolean result_;
+    Marker marker_ = enter_section_(builder_);
+    result_ = SectionNameStart_0(builder_, level_ + 1);
+    result_ = result_ && consumeToken(builder_, STRING);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  // '$'?
+  private static boolean SectionNameStart_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "SectionNameStart_0")) return false;
+    consumeToken(builder_, "$");
+    return true;
+  }
+
+  /* ********************************************************** */
   // StrongStar | StrongUnderscore
   public static boolean Strong(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Strong")) return false;
@@ -1383,34 +1527,11 @@
   }
 
   /* ********************************************************** */
-  // (OptionalSpace Newline <<eof>>) | (OptionalSpace <<eof>>)
+  // OptionalSpace <<eof>>
   static boolean TerminalEndline(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "TerminalEndline")) return false;
     boolean result_;
     Marker marker_ = enter_section_(builder_);
-    result_ = TerminalEndline_0(builder_, level_ + 1);
-    if (!result_) result_ = TerminalEndline_1(builder_, level_ + 1);
-    exit_section_(builder_, marker_, null, result_);
-    return result_;
-  }
-
-  // OptionalSpace Newline <<eof>>
-  private static boolean TerminalEndline_0(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "TerminalEndline_0")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_);
-    result_ = OptionalSpace(builder_, level_ + 1);
-    result_ = result_ && consumeToken(builder_, NEWLINE);
-    result_ = result_ && eof(builder_, level_ + 1);
-    exit_section_(builder_, marker_, null, result_);
-    return result_;
-  }
-
-  // OptionalSpace <<eof>>
-  private static boolean TerminalEndline_1(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "TerminalEndline_1")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_);
     result_ = OptionalSpace(builder_, level_ + 1);
     result_ = result_ && eof(builder_, level_ + 1);
     exit_section_(builder_, marker_, null, result_);
@@ -1418,47 +1539,6 @@
   }
 
   /* ********************************************************** */
-  // VerbatimItem+
-  public static boolean Verbatim(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "Verbatim")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<verbatim>");
-    result_ = VerbatimItem(builder_, level_ + 1);
-    int pos_ = current_position_(builder_);
-    while (result_) {
-      if (!VerbatimItem(builder_, level_ + 1)) break;
-      if (!empty_element_parsed_guard_(builder_, "Verbatim", pos_)) break;
-      pos_ = current_position_(builder_);
-    }
-    exit_section_(builder_, level_, marker_, VERBATIM, result_, false, null);
-    return result_;
-  }
-
-  /* ********************************************************** */
-  // BlankLine* NonblankIndentedLine
-  public static boolean VerbatimItem(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "VerbatimItem")) return false;
-    boolean result_;
-    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<verbatim item>");
-    result_ = VerbatimItem_0(builder_, level_ + 1);
-    result_ = result_ && NonblankIndentedLine(builder_, level_ + 1);
-    exit_section_(builder_, level_, marker_, VERBATIM_ITEM, result_, false, null);
-    return result_;
-  }
-
-  // BlankLine*
-  private static boolean VerbatimItem_0(PsiBuilder builder_, int level_) {
-    if (!recursion_guard_(builder_, level_, "VerbatimItem_0")) return false;
-    int pos_ = current_position_(builder_);
-    while (true) {
-      if (!BlankLine(builder_, level_ + 1)) break;
-      if (!empty_element_parsed_guard_(builder_, "VerbatimItem_0", pos_)) break;
-      pos_ = current_position_(builder_);
-    }
-    return true;
-  }
-
-  /* ********************************************************** */
   // Spacechar | Newline
   public static boolean Whitespace(PsiBuilder builder_, int level_) {
     if (!recursion_guard_(builder_, level_, "Whitespace")) return false;
diff --git a/src/Markdown/_MarkdownLexer.flex b/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.flex
similarity index 94%
rename from src/Markdown/_MarkdownLexer.flex
rename to gen/org/jetbrains/markdown/lexer/_MarkdownLexer.flex
index 0a15e41..f5aab89 100644
--- a/src/Markdown/_MarkdownLexer.flex
+++ b/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.flex
@@ -22,7 +22,7 @@
 Newline="\r"|"\n"|"\r\n"
 Spacechar=[\ \t\f]
 Number=[0-9]+(\.[0-9]*)?
-String=[^~\*_`&\[\]()<!#\\ \t\n\r]+
+String=[^~:{}$\*_`&\[\]()<!#\\ \t\n\r]+
 AnyChar=.
 Line=!'\r' !'\n' .* {Newline}
 
@@ -32,8 +32,10 @@
   {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/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.java b/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.java
index 0052e3e..c84a83d 100644
--- a/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.java
+++ b/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.java
@@ -1,4 +1,4 @@
-/* The following code was generated by JFlex 1.4.3 on 9/25/14 10:26 PM */
+/* The following code was generated by JFlex 1.4.3 on 9/29/14 3:24 PM */
 
 package org.jetbrains.markdown.lexer;
 
@@ -10,8 +10,8 @@
 /**
  * This class is a scanner generated by 
  * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
- * on 9/25/14 10:26 PM from the specification file
- * <tt>/Users/orangy/Projects/dokka/src/Markdown/_MarkdownLexer.flex</tt>
+ * on 9/29/14 3:24 PM from the specification file
+ * <tt>/Users/orangy/Projects/dokka/gen/org/jetbrains/markdown/lexer/_MarkdownLexer.flex</tt>
  */
 public class _MarkdownLexer implements FlexLexer {
   /** initial size of the lookahead buffer */
@@ -35,9 +35,9 @@
    */
   private static final String ZZ_CMAP_PACKED = 
     "\11\0\1\7\1\2\1\0\1\3\1\1\22\0\1\7\1\6\1\0"+
-    "\1\6\2\0\1\6\1\0\3\6\3\0\1\5\1\0\2\4\1\14"+
-    "\1\11\1\4\1\12\1\4\1\13\2\4\2\0\1\6\36\0\1\6"+
-    "\1\10\1\6\1\0\2\6\35\0\1\6\uff81\0";
+    "\2\6\1\0\1\6\1\0\3\6\3\0\1\5\1\0\2\4\1\14"+
+    "\1\11\1\4\1\12\1\4\1\13\2\4\1\6\1\0\1\6\36\0"+
+    "\1\6\1\10\1\6\1\0\2\6\32\0\1\6\1\0\2\6\uff81\0";
 
   /** 
    * Translates characters to character classes
@@ -245,7 +245,7 @@
     char [] map = new char[0x10000];
     int i = 0;  /* index in packed string  */
     int j = 0;  /* index in unpacked array */
-    while (i < 74) {
+    while (i < 80) {
       int  count = packed.charAt(i++);
       char value = packed.charAt(i++);
       do map[j++] = value; while (--count > 0);
diff --git a/src/Analysis/CommentsAPI.kt b/src/Analysis/CommentsAPI.kt
index 5df4bd3..a17b6aa 100644
--- a/src/Analysis/CommentsAPI.kt
+++ b/src/Analysis/CommentsAPI.kt
@@ -31,5 +31,5 @@
             comment.substring(0, comment.length - 2)
         else
             comment).trim()
-    }.filter { it.any() }.join("\n")
+    }.join("\n")
 }
\ No newline at end of file
diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt
index d7530a2..3b87835 100644
--- a/src/Formats/MarkdownFormatService.kt
+++ b/src/Formats/MarkdownFormatService.kt
@@ -16,10 +16,6 @@
         return text.htmlEscape()
     }
 
-    override public fun formatText(text: RichString): String {
-        return text.toString().htmlEscape()
-    }
-
     override public fun formatCode(code: String): String {
         return "`$code`"
     }
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index 0c58f55..b1e614f 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -28,19 +28,17 @@
     public abstract fun formatCode(code: String): String
     public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
 
-    open fun formatText(text: RichString): String {
+    open fun formatText(nodes: Iterable<ContentNode>): String {
+        return nodes.map { formatText(it) }.join("")
+    }
+
+    open fun formatText(text: ContentNode): String {
         return StringBuilder {
-            for (slice in text.slices) {
-                val style = slice.style
-                when (style) {
-                    is NormalStyle -> append(slice.text)
-                    is BoldStyle -> append(formatBold(slice.text))
-                    is CodeStyle -> append(formatCode(slice.text))
-                    is LinkStyle -> {
-                        val node = resolutionService.resolve(style.link)
-                        val location = locationService.location(node)
-                        append(formatLink(slice.text, location))
-                    }
+            for (node in text.children) {
+                when (node) {
+                    is ContentText -> append(node.text)
+                    is ContentEmphasis -> append(formatBold(formatText(node.children)))
+                    else -> append(formatText(node.children))
                 }
             }
         }.toString()
@@ -67,7 +65,7 @@
                     if (label.startsWith("$"))
                         continue
                     appendLine(to, formatBold(formatText(label)))
-                    appendLine(to, formatText(section.text))
+                    appendLine(to, formatText(section))
                     appendLine(to)
                 }
             }
diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt
index 77a0bb6..8fea5a6 100644
--- a/src/Formats/TextFormatService.kt
+++ b/src/Formats/TextFormatService.kt
@@ -9,12 +9,9 @@
                 appendln(signatureGenerator.render(node))
                 appendln()
                 appendln(node.doc.summary)
-                for (n in 0..node.doc.summary.length())
-                    append("=")
 
                 for ((label,section) in node.doc.sections) {
                     appendln(label)
-                    appendln(section.text)
                 }
             }
         }
diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt
new file mode 100644
index 0000000..78bd7ea
--- /dev/null
+++ b/src/Kotlin/ContentBuilder.kt
@@ -0,0 +1,79 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.markdown.MarkdownElementTypes
+import java.util.ArrayDeque
+
+public fun MarkdownTree.toContent(): Content {
+    val nodeStack = ArrayDeque<ContentNode>()
+    nodeStack.push(Content())
+
+    visit {(node, text, processChildren) ->
+        val parent = nodeStack.peek()!!
+        val nodeType = node.getTokenType()
+        val nodeText = getNodeText(node)
+        when (nodeType) {
+            MarkdownElementTypes.BULLET_LIST -> {
+                nodeStack.push(ContentList())
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.ORDERED_LIST -> {
+                nodeStack.push(ContentList()) // TODO: add list kind
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.HORIZONTAL_RULE -> {
+            }
+            MarkdownElementTypes.LIST_BLOCK -> {
+                nodeStack.push(ContentBlock())
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.EMPH -> {
+                nodeStack.push(ContentEmphasis())
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.STRONG -> {
+                nodeStack.push(ContentStrong())
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.ANONYMOUS_SECTION -> {
+                nodeStack.push(ContentSection(""))
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.NAMED_SECTION -> {
+                val label = findChildByType(node, MarkdownElementTypes.SECTION_NAME)?.let { getNodeText(it) } ?: ""
+                nodeStack.push(ContentSection(label))
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.PLAIN_TEXT -> {
+                nodeStack.push(ContentText(nodeText))
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.END_LINE -> {
+                nodeStack.push(ContentText(nodeText))
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            MarkdownElementTypes.BLANK_LINE -> {
+                processChildren()
+            }
+            MarkdownElementTypes.PARA -> {
+                nodeStack.push(ContentBlock())
+                processChildren()
+                parent.children.add(nodeStack.pop())
+            }
+            else -> {
+                processChildren()
+            }
+        }
+    }
+    return nodeStack.pop() as Content
+}
+
+
diff --git a/src/Model/Diagnostics.kt b/src/Kotlin/Diagnostics.kt
similarity index 92%
rename from src/Model/Diagnostics.kt
rename to src/Kotlin/Diagnostics.kt
index 1077da7..5548bc0 100644
--- a/src/Model/Diagnostics.kt
+++ b/src/Kotlin/Diagnostics.kt
@@ -4,11 +4,11 @@
 import org.jetbrains.jet.lang.resolve.name.*
 import org.jetbrains.jet.lang.resolve.BindingContext
 
-fun BindingContext.checkResolveChildren(node: DocumentationNode) {
+fun DocumentationContext.checkResolveChildren(node: DocumentationNode) {
     if (node.kind != DocumentationNode.Kind.Module && node.kind != DocumentationNode.Kind.Package) {
         // TODO: we don't resolve packages and modules for now
 
-        val parentScope = getResolutionScope(node.descriptor)
+        val parentScope = getResolutionScope(node)
         for (item in node.details + node.members) {
             val symbolName = item.name
             val symbol: DeclarationDescriptor? = when (item.kind) {
diff --git a/src/Model/DocumentationBuildingVisitor.kt b/src/Kotlin/DocumentationBuildingVisitor.kt
similarity index 99%
rename from src/Model/DocumentationBuildingVisitor.kt
rename to src/Kotlin/DocumentationBuildingVisitor.kt
index 6118b0f..5654903 100644
--- a/src/Model/DocumentationBuildingVisitor.kt
+++ b/src/Kotlin/DocumentationBuildingVisitor.kt
@@ -5,6 +5,7 @@
 import org.jetbrains.jet.lang.resolve.*
 
 public data class DocumentationOptions(val includeNonPublic : Boolean = false)
+
 class DocumentationBuildingVisitor(val context: BindingContext,
                                    val options: DocumentationOptions,
                                    private val worker: DeclarationDescriptorVisitor<DocumentationNode, DocumentationNode>)
diff --git a/src/Kotlin/DocumentationContext.kt b/src/Kotlin/DocumentationContext.kt
new file mode 100644
index 0000000..1491aa2
--- /dev/null
+++ b/src/Kotlin/DocumentationContext.kt
@@ -0,0 +1,47 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
+import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.lang.resolve.scopes.JetScope
+import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
+import org.jetbrains.jet.lang.resolve.name.FqName
+
+public class DocumentationContext(val bindingContext: BindingContext) {
+    val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
+    val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
+
+    fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
+        descriptorToNode.put(descriptor, node)
+        nodeToDescriptor.put(node, descriptor)
+    }
+
+    fun getResolutionScope(node: DocumentationNode): JetScope {
+        val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
+        return bindingContext.getResolutionScope(descriptor)
+    }
+
+    fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
+        val docText = bindingContext.getDocumentationElements(descriptor).map { it.extractText() }.join("\n")
+        val tree = MarkdownProcessor.parse(docText)
+        println(tree.toTestString())
+        val content = tree.toContent()
+        return content
+    }
+}
+
+fun BindingContext.createDocumentationModule(name: String,
+                                             module: ModuleDescriptor,
+                                             packages: Set<FqName>,
+                                             options: DocumentationOptions = DocumentationOptions()): DocumentationModule {
+    val documentationModule = DocumentationModule(name)
+    val context = DocumentationContext(this)
+    val visitor = DocumentationNodeBuilder(context)
+    for (packageName in packages) {
+        val pkg = module.getPackage(packageName)
+        pkg!!.accept(DocumentationBuildingVisitor(this, options, visitor), documentationModule)
+    }
+
+    // TODO: Uncomment for resolve verification
+    // checkResolveChildren(documentationModule)
+    return documentationModule
+}
diff --git a/src/Kotlin/DocumentationNodeBuilder.kt b/src/Kotlin/DocumentationNodeBuilder.kt
new file mode 100644
index 0000000..535f037
--- /dev/null
+++ b/src/Kotlin/DocumentationNodeBuilder.kt
@@ -0,0 +1,177 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
+import org.jetbrains.jet.lang.descriptors.MemberDescriptor
+import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
+import org.jetbrains.jet.lang.types.JetType
+import org.jetbrains.jet.lang.descriptors.Named
+import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
+import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
+import org.jetbrains.jet.lang.descriptors.ClassDescriptor
+import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
+import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
+import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
+import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
+import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
+import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor
+import org.jetbrains.jet.lang.descriptors.ClassKind
+import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
+
+class DocumentationNodeBuilder(val context: DocumentationContext) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DocumentationNode>() {
+
+    fun reference(from: DocumentationNode, to: DocumentationNode, kind: DocumentationReference.Kind) {
+        from.addReferenceTo(to, kind)
+        if (kind == DocumentationReference.Kind.Link)
+            to.addReferenceTo(from, DocumentationReference.Kind.Link)
+        else
+            to.addReferenceTo(from, DocumentationReference.Kind.Owner)
+    }
+
+    fun addModality(descriptor: MemberDescriptor, data: DocumentationNode) {
+        val modifier = descriptor.getModality().name().toLowerCase()
+        val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
+        reference(data, node, DocumentationReference.Kind.Detail)
+    }
+
+    fun addVisibility(descriptor: MemberDescriptor, data: DocumentationNode) {
+        val modifier = descriptor.getVisibility().toString()
+        val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
+        reference(data, node, DocumentationReference.Kind.Detail)
+    }
+
+    fun addType(descriptor: DeclarationDescriptor, t: JetType?, data: DocumentationNode) {
+        if (t == null)
+            return
+        val typeConstructor = t.getConstructor()
+        val classifierDescriptor = typeConstructor.getDeclarationDescriptor()
+        val name = when (classifierDescriptor) {
+            is Named -> classifierDescriptor.getName().asString()
+            else -> "<anonymous>"
+        }
+        val node = DocumentationNode(name, Content.Empty, DocumentationNode.Kind.Type)
+        reference(data, node, DocumentationReference.Kind.Detail)
+
+        for (param in t.getArguments())
+            addType(descriptor, param.getType(), node)
+    }
+
+    override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Unknown)
+        reference(data!!, node, DocumentationReference.Kind.Link)
+        context.register(descriptor, node)
+        return node
+    }
+
+    override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val node = DocumentationNode(descriptor.getName().asString(), Content.Empty, DocumentationNode.Kind.Receiver)
+        reference(data!!, node, DocumentationReference.Kind.Detail)
+
+        addType(descriptor, descriptor.getType(), node)
+
+        return node
+    }
+
+    override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Parameter)
+        reference(data!!, node, DocumentationReference.Kind.Detail)
+
+        addType(descriptor, descriptor.getType(), node)
+
+        return node
+    }
+
+    override fun visitClassDescriptor(descriptor: ClassDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, when (descriptor.getKind()) {
+            ClassKind.OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object
+            ClassKind.CLASS_OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object
+            ClassKind.TRAIT -> org.jetbrains.dokka.DocumentationNode.Kind.Interface
+            ClassKind.ENUM_CLASS -> org.jetbrains.dokka.DocumentationNode.Kind.Enum
+            ClassKind.ENUM_ENTRY -> org.jetbrains.dokka.DocumentationNode.Kind.EnumItem
+            else -> DocumentationNode.Kind.Class
+        })
+        reference(data!!, node, DocumentationReference.Kind.Member)
+        addModality(descriptor, node)
+        addVisibility(descriptor, node)
+        context.register(descriptor, node)
+        return node
+    }
+
+    override fun visitFunctionDescriptor(descriptor: FunctionDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Function)
+        reference(data!!, node, DocumentationReference.Kind.Member)
+
+        addType(descriptor, descriptor.getReturnType(), node)
+        addModality(descriptor, node)
+        addVisibility(descriptor, node)
+        context.register(descriptor, node)
+        return node
+    }
+
+    override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.TypeParameter)
+        reference(data!!, node, DocumentationReference.Kind.Detail)
+        val builtIns = KotlinBuiltIns.getInstance()
+        for (constraint in descriptor.getUpperBounds()) {
+            if (constraint == builtIns.getDefaultBound())
+                continue
+            val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
+            reference(node, constraintNode, DocumentationReference.Kind.Detail)
+        }
+        for (constraint in descriptor.getLowerBounds()) {
+            if (builtIns.isNothing(constraint))
+                continue
+            val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
+            reference(node, constraintNode, DocumentationReference.Kind.Detail)
+        }
+        return node
+    }
+
+    override fun visitPropertyDescriptor(descriptor: PropertyDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Property)
+        reference(data!!, node, DocumentationReference.Kind.Member)
+
+        addType(descriptor, descriptor.getType(), node)
+        addModality(descriptor, node)
+        addVisibility(descriptor, node)
+        context.register(descriptor, node)
+        return node
+    }
+
+    override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val doc = context.parseDocumentation(descriptor)
+        val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Constructor)
+        reference(data!!, node, DocumentationReference.Kind.Member)
+
+        addVisibility(descriptor, node)
+        context.register(descriptor, node)
+        return node
+    }
+
+    override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val node = DocumentationNode(descriptor.getFqName().asString(), Content.Empty, DocumentationNode.Kind.Package)
+        reference(data!!, node, DocumentationReference.Kind.Member)
+        return node
+    }
+
+    override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor?, data: DocumentationNode?): DocumentationNode? {
+        descriptor!!
+        val node = DocumentationNode(descriptor.fqName.asString(), Content.Empty, DocumentationNode.Kind.Package)
+        reference(data!!, node, DocumentationReference.Kind.Member)
+        return node
+    }
+}
\ No newline at end of file
diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt
index fe6e843..19d5a8f 100644
--- a/src/Markdown/MarkdownProcessor.kt
+++ b/src/Markdown/MarkdownProcessor.kt
@@ -9,12 +9,11 @@
 import com.intellij.util.diff.FlyweightCapableTreeStructure
 import com.intellij.openapi.util.Ref
 import org.jetbrains.markdown.lexer.MarkdownLexer
+import com.intellij.psi.tree.IElementType
 
-public class MarkdownProcessor {
-    class object {
-        val EXPR_LANGUAGE = object : Language("MARKDOWN") {}
-        val DOCUMENT = IFileElementType("DOCUMENT", EXPR_LANGUAGE);
-    }
+public object MarkdownProcessor {
+    val EXPR_LANGUAGE = object : Language("MARKDOWN") {}
+    val DOCUMENT = IFileElementType("DOCUMENT", EXPR_LANGUAGE);
 
     public fun parse(markdown: String): MarkdownTree {
         val parser = MarkdownParser()
@@ -30,6 +29,28 @@
         visit(structure.getRoot(), action)
     }
 
+    fun findChildByType(node: LighterASTNode, findType: IElementType) : LighterASTNode? {
+        val ref = Ref.create<Array<LighterASTNode>?>()
+        val count = structure.getChildren(node, ref)
+        val children = ref.get()
+        if (children != null) {
+            for (index in 0..count - 1) {
+                val child = children[index]
+                val nodeType = child.getTokenType()
+                if (nodeType == findType)
+                    return child
+                val nestedChild = findChildByType(child, findType)
+                if (nestedChild != null)
+                    return nestedChild
+            }
+        }
+        return null
+    }
+
+    fun getNodeText(node: LighterASTNode) : String {
+        return text.substring(node.getStartOffset(), node.getEndOffset())
+    }
+
     fun visit(node: LighterASTNode, action: (LighterASTNode, String, visitChildren: () -> Unit) -> Unit) {
         action(node, text) {
             val ref = Ref.create<Array<LighterASTNode>?>()
@@ -46,7 +67,7 @@
 
 }
 
-public fun MarkdownTree.dump(): String {
+public fun MarkdownTree.toTestString(): String {
     val sb = StringBuilder()
     var level = 0
     visit {(node, text, visitChildren) ->
@@ -64,29 +85,25 @@
 
 public fun MarkdownTree.toHtml(): String {
     val sb = StringBuilder()
-    var level = 0
     visit {(node, text, processChildren) ->
         val nodeType = node.getTokenType()
         val nodeText = text.substring(node.getStartOffset(), node.getEndOffset())
-        val indent = " ".repeat(level * 2)
         when (nodeType) {
             MarkdownElementTypes.BULLET_LIST -> {
-                sb.appendln("$indent<ul>")
-                level++
+                sb.appendln("<ul>")
                 processChildren()
-                level--
-                sb.appendln("$indent</ul>")
+                sb.appendln("</ul>")
             }
             MarkdownElementTypes.HORIZONTAL_RULE -> {
-                sb.appendln("$indent<hr/>")
+                sb.appendln("<hr/>")
             }
             MarkdownElementTypes.ORDERED_LIST -> {
-                sb.appendln("$indent<ol>")
+                sb.appendln("<ol>")
                 processChildren()
-                sb.appendln("$indent</ol>")
+                sb.appendln("</ol>")
             }
             MarkdownElementTypes.LIST_BLOCK -> {
-                sb.append("$indent<li>")
+                sb.append("<li>")
                 processChildren()
                 sb.appendln("</li>")
             }
@@ -110,14 +127,9 @@
                 sb.appendln()
             }
             MarkdownElementTypes.PARA -> {
-                sb.appendln("$indent<p>")
+                sb.append("<p>")
                 processChildren()
-                sb.appendln("$indent</p>")
-            }
-            MarkdownElementTypes.VERBATIM -> {
-                sb.appendln("$indent<pre><code>")
-                processChildren()
-                sb.appendln("$indent</code></pre>")
+                sb.appendln("</p>")
             }
             else -> {
                 processChildren()
@@ -129,8 +141,8 @@
 
 
 fun markdownToHtml(markdown: String): String {
-    val markdownTree = MarkdownProcessor().parse(markdown)
-    val ast = markdownTree.dump()
+    val markdownTree = MarkdownProcessor.parse(markdown)
+    val ast = markdownTree.toTestString()
     return markdownTree.toHtml()
 }
 
diff --git a/src/Markdown/markdown.bnf b/src/Markdown/markdown.bnf
index ca25429..79e9e0b 100644
--- a/src/Markdown/markdown.bnf
+++ b/src/Markdown/markdown.bnf
@@ -21,35 +21,35 @@
 
 }
 
-Document ::= BOM? ( Block )*
+Document ::= BOM? Whitespace* AnonymousSection? (Whitespace* NamedSection)*
 
-private OptionalSpace ::= Spacechar*
-private RequiredSpace ::= Spacechar+
-private NonindentSpace ::= ("   " | "  " | " ")?
+AnonymousSection ::= SectionBody
+NamedSection ::= SectionHeader SectionBody
+private SectionHeader ::= '$' SectionName OptionalSpace ':' OptionalSpace
+SectionName ::= SectionNameStart | '{' OptionalSpace SectionNameStart (Spacechar+ String)* OptionalSpace '}'
+private SectionNameStart ::= '$'? String
+SectionBody::= Block*
 
 BlankLine ::= OptionalSpace Newline
 
 Whitespace ::= Spacechar | Newline
+private OptionalSpace ::= Spacechar*
+private NonindentSpace ::= ("   " | "  " | " ")?
+
 EndLine ::= TerminalEndline | NormalEndline
-private NormalEndline ::=   OptionalSpace Newline !BlankLine
-private TerminalEndline ::= (OptionalSpace Newline <<eof>>) | (OptionalSpace <<eof>>)
+private NormalEndline ::=   Newline !BlankLine
+private TerminalEndline ::= OptionalSpace <<eof>>
 private Indent ::=            "\t" | "    "
-NonblankIndentedLine ::= !BlankLine IndentedLine
-IndentedLine ::= Indent PlainText
 
 // ---- BLOCKS ----
-private Block ::= BlankLine* (
-          Para
-        | Verbatim
-        | OrderedList
-        | BulletList
-        | Inlines
-        )
+Block ::= BlankLine* (
+              OrderedList
+            | BulletList
+            | HorizontalRule
+            | Para
+            )
 
-Para ::= NonindentSpace Inlines (BlankLine | TerminalEndline)
-
-Verbatim ::= VerbatimItem+
-VerbatimItem ::= BlankLine* NonblankIndentedLine
+Para ::= Inlines (BlankLine | TerminalEndline)?
 
 HorizontalRule ::= NonindentSpace
                  ( '*' OptionalSpace '*' OptionalSpace '*' (OptionalSpace '*')*
@@ -74,8 +74,8 @@
 
 // ---- INLINES ----
 private Inlines ::= (!EndLine Inline | EndLine &Inline )+ EndLine?
-private Inline  ::= Strong | Emph | Link | PlainText
-PlainText ::= (String | Number | Spacechar)+
+Inline  ::= Strong | Emph | Link | PlainText
+PlainText ::= (String | Number | Spacechar+)+
 
 Emph ::= EmphStar | EmphUnderscore
 private EmphStar ::= '*' !Whitespace (!'*' Inline)+ '*'
diff --git a/src/Model/Content.kt b/src/Model/Content.kt
new file mode 100644
index 0000000..eb092cb
--- /dev/null
+++ b/src/Model/Content.kt
@@ -0,0 +1,88 @@
+package org.jetbrains.dokka
+
+import kotlin.properties.Delegates
+
+public abstract class ContentNode {
+    val children = arrayListOf<ContentNode>()
+
+    class object {
+        val empty = ContentEmpty
+    }
+
+    fun isEmpty() = children.isEmpty()
+}
+
+public object ContentEmpty : ContentNode( )
+
+public class ContentText(val text : String) : ContentNode( )
+public class ContentBlock() : ContentNode( )
+public class ContentEmphasis() : ContentNode()
+public class ContentStrong() : ContentNode()
+public class ContentList() : ContentNode()
+public class ContentSection(public val label: String) : ContentNode()
+
+public class Content() : ContentNode() {
+    public val sections: Map<String, ContentSection> by Delegates.lazy {
+        val map = hashMapOf<String, ContentSection>()
+        for (child in children) {
+            if (child is ContentSection)
+                map.put(child.label, child)
+        }
+
+        if ("\$summary" !in map && "\$description" !in map) {
+            // no explicit summary and description, convert anonymous section
+            val anonymous = map[""]
+            if (anonymous != null) {
+                map.remove("")
+                val summary = ContentSection("\$summary")
+                val description = ContentSection("\$description")
+
+                val summaryNodes = anonymous.children.take(1)
+                val descriptionNodes = anonymous.children.drop(1)
+
+                if (summaryNodes.any()) {
+                    summary.children.addAll(summaryNodes)
+                    map.put("\$summary", summary)
+                }
+
+                if (descriptionNodes.any()) {
+                    description.children.addAll(descriptionNodes)
+                    map.put("\$description", description)
+                }
+            }
+        }
+        map
+    }
+
+    public val summary: ContentNode get() = sections["\$summary"] ?: ContentNode.empty
+    public val description: ContentNode get() = sections["\$description"] ?: ContentNode.empty
+
+    override fun equals(other: Any?): Boolean {
+        if (other !is Content)
+            return false
+        if (sections.size != other.sections.size)
+            return false
+        for (keys in sections.keySet())
+            if (sections[keys] != other.sections[keys])
+                return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return sections.map { it.hashCode() }.sum()
+    }
+
+    override fun toString(): String {
+        if (sections.isEmpty())
+            return "<empty>"
+        return sections.values().joinToString()
+    }
+
+    val isEmpty: Boolean
+        get() = sections.none()
+
+    class object {
+        val Empty = Content()
+    }
+}
diff --git a/src/Model/DocumentationContent.kt b/src/Model/DocumentationContent.kt
deleted file mode 100644
index 77e8c76..0000000
--- a/src/Model/DocumentationContent.kt
+++ /dev/null
@@ -1,150 +0,0 @@
-package org.jetbrains.dokka
-
-import org.jetbrains.jet.lang.descriptors.*
-import org.jetbrains.jet.lang.resolve.BindingContext
-
-public class DocumentationContentSection(public val label: String, public val text: RichString) {
-    override fun toString(): String {
-        return "$label = $text"
-    }
-}
-
-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 (sections.size != other.sections.size)
-            return false
-        for (keys in sections.keySet())
-            if (sections[keys] != other.sections[keys])
-                return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        return sections.map { it.hashCode() }.sum()
-    }
-
-    override fun toString(): String {
-        if (sections.isEmpty())
-            return "<empty>"
-        return sections.values().joinToString()
-    }
-
-    val isEmpty: Boolean
-        get() = description.isEmpty() && sections.none()
-
-    class object {
-        val Empty = DocumentationContent(mapOf())
-    }
-}
-
-
-fun BindingContext.getDocumentation(descriptor: DeclarationDescriptor): DocumentationContent {
-    val docText = getDocumentationElements(descriptor).map { it.extractText() }.join("\n")
-    val sections = docText.parseSections()
-    sections.createSummaryAndDescription()
-    return DocumentationContent(sections)
-}
-
-fun MutableMap<String, DocumentationContentSection>.createSummaryAndDescription() {
-
-    val summary = get("\$summary")
-    val description = get("\$description")
-    if (summary != null && description == null) {
-        return
-    }
-
-    if (summary == null && description != null) {
-        return
-    }
-
-    val unnamed = get("")
-    if (unnamed == null) {
-        return
-    }
-
-    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> {
-    val c = get(index)
-    when {
-        Character.isJavaIdentifierStart(c) -> {
-            for (end in index + 1..length - 1) {
-                if (!Character.isJavaIdentifierPart(get(end))) {
-                    return substring(index, end) to end
-                }
-            }
-            return substring(index, length) to length
-        }
-        c == '$' -> {
-            for (end in index + 1..length - 1) {
-                if (Character.isWhitespace(get(end))) {
-                    return substring(index, end) to end
-                }
-            }
-            return substring(index, length) to length
-        }
-        c == '{' -> {
-            val end = indexOf('}', index + 1)
-            return substring(index + 1, end) to end + 1
-        }
-    }
-    return "" to -1
-}
-
-fun String.parseSections(): MutableMap<String, DocumentationContentSection> {
-    val sections = hashMapOf<String, DocumentationContentSection>()
-    var currentLabel = ""
-    var currentSectionStart = 0
-    var currentIndex = 0
-
-    while (currentIndex < length) {
-        if (get(currentIndex) == '$') {
-            val (label, index) = parseLabel(currentIndex + 1)
-            if (index != -1 && index < length() && get(index) == ':') {
-                // section starts, add previous section
-                val currentContent = substring(currentSectionStart, currentIndex).trim()
-                val section = DocumentationContentSection(currentLabel, currentContent.toRichString())
-                sections.put(section.label, section)
-
-                currentLabel = label
-                currentIndex = index + 1
-                currentSectionStart = currentIndex
-                continue
-            }
-        }
-        currentIndex++
-
-    }
-
-    val currentContent = substring(currentSectionStart, currentIndex).trim()
-    val section = DocumentationContentSection(currentLabel, currentContent.toRichString())
-    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/DocumentationModule.kt b/src/Model/DocumentationModule.kt
index 78ebda0..6084ea5 100644
--- a/src/Model/DocumentationModule.kt
+++ b/src/Model/DocumentationModule.kt
@@ -1,30 +1,11 @@
 package org.jetbrains.dokka
 
-import org.jetbrains.jet.lang.resolve.BindingContext
-import org.jetbrains.jet.lang.descriptors.*
-import org.jetbrains.jet.lang.resolve.name.FqName
-
-public class DocumentationModule(name: String, val module: ModuleDescriptor) : DocumentationNode(module, name, DocumentationContent.Empty, DocumentationNode.Kind.Module) {
+public class DocumentationModule(name: String) : DocumentationNode(name, Content.Empty, DocumentationNode.Kind.Module) {
     fun merge(other: DocumentationModule): DocumentationModule {
-        val model = DocumentationModule(name, module)
+        val model = DocumentationModule(name)
         model.addAllReferencesFrom(other)
         model.addAllReferencesFrom(this)
         return model
     }
 }
 
-fun BindingContext.createDocumentationModule(name: String,
-                                             module: ModuleDescriptor,
-                                             packages: Set<FqName>,
-                                             options : DocumentationOptions = DocumentationOptions()): DocumentationModule {
-    val documentationModule = DocumentationModule(name, module)
-    val visitor = DocumentationNodeBuilder(this)
-    for (packageName in packages) {
-        val pkg = module.getPackage(packageName)
-        pkg!!.accept(DocumentationBuildingVisitor(this, options, visitor), documentationModule)
-    }
-
-    // TODO: Uncomment for resolve verification
-    // checkResolveChildren(documentationModule)
-    return documentationModule
-}
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index c96c383..198e549 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -1,12 +1,9 @@
 package org.jetbrains.dokka
 
-import org.jetbrains.jet.lang.descriptors.*
 import java.util.LinkedHashSet
 
-
-public open class DocumentationNode(val descriptor: DeclarationDescriptor,
-                                    val name: String,
-                                    val doc: DocumentationContent,
+public open class DocumentationNode(val name: String,
+                                    val doc: Content,
                                     val kind: DocumentationNode.Kind) {
 
     private val references = LinkedHashSet<DocumentationReference>()
diff --git a/src/Model/DocumentationNodeBuilder.kt b/src/Model/DocumentationNodeBuilder.kt
deleted file mode 100644
index f724c44..0000000
--- a/src/Model/DocumentationNodeBuilder.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package org.jetbrains.dokka
-
-import org.jetbrains.jet.lang.resolve.*
-import org.jetbrains.jet.lang.descriptors.*
-import org.jetbrains.jet.lang.descriptors.impl.*
-import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
-import org.jetbrains.jet.lang.types.JetType
-
-class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DocumentationNode>() {
-
-    fun reference(from: DocumentationNode, to: DocumentationNode, kind: DocumentationReference.Kind) {
-        from.addReferenceTo(to, kind)
-        if (kind == DocumentationReference.Kind.Link)
-            to.addReferenceTo(from, DocumentationReference.Kind.Link)
-        else
-            to.addReferenceTo(from, DocumentationReference.Kind.Owner)
-    }
-
-    fun addModality(descriptor: MemberDescriptor, data: DocumentationNode) {
-        val modifier = descriptor.getModality().name().toLowerCase()
-        val node = DocumentationNode(descriptor, modifier, DocumentationContent.Empty, DocumentationNode.Kind.Modifier)
-        reference(data, node, DocumentationReference.Kind.Detail)
-    }
-
-    fun addVisibility(descriptor: MemberDescriptor, data: DocumentationNode) {
-        val modifier = descriptor.getVisibility().toString()
-        val node = DocumentationNode(descriptor, modifier, DocumentationContent.Empty, DocumentationNode.Kind.Modifier)
-        reference(data, node, DocumentationReference.Kind.Detail)
-    }
-
-    fun addType(descriptor: DeclarationDescriptor, t: JetType?, data: DocumentationNode) {
-        if (t == null)
-            return
-        val typeConstructor = t.getConstructor()
-        val classifierDescriptor = typeConstructor.getDeclarationDescriptor()
-        val name = when (classifierDescriptor) {
-            is Named -> classifierDescriptor.getName().asString()
-            else -> "<anonymous>"
-        }
-        val node = DocumentationNode(descriptor, name, DocumentationContent.Empty, DocumentationNode.Kind.Type)
-        reference(data, node, DocumentationReference.Kind.Detail)
-
-        for (param in t.getArguments())
-            addType(descriptor, param.getType(), node)
-    }
-
-    override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Unknown)
-        reference(data!!, node, DocumentationReference.Kind.Link)
-        return node
-    }
-
-    override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val node = DocumentationNode(descriptor!!, descriptor.getName().asString(), DocumentationContent.Empty, DocumentationNode.Kind.Receiver)
-        reference(data!!, node, DocumentationReference.Kind.Detail)
-
-        addType(descriptor, descriptor.getType(), node)
-
-        return node
-    }
-
-    override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Parameter)
-        reference(data!!, node, DocumentationReference.Kind.Detail)
-
-        addType(descriptor, descriptor.getType(), node)
-
-        return node
-    }
-
-    override fun visitClassDescriptor(descriptor: ClassDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, when (descriptor.getKind()) {
-            ClassKind.OBJECT -> DocumentationNode.Kind.Object
-            ClassKind.CLASS_OBJECT -> DocumentationNode.Kind.Object
-            ClassKind.TRAIT -> DocumentationNode.Kind.Interface
-            ClassKind.ENUM_CLASS -> DocumentationNode.Kind.Enum
-            ClassKind.ENUM_ENTRY -> DocumentationNode.Kind.EnumItem
-            else -> DocumentationNode.Kind.Class
-        })
-        reference(data!!, node, DocumentationReference.Kind.Member)
-        addModality(descriptor, node)
-        addVisibility(descriptor, node)
-        return node
-    }
-
-    override fun visitFunctionDescriptor(descriptor: FunctionDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Function)
-        reference(data!!, node, DocumentationReference.Kind.Member)
-
-        addType(descriptor, descriptor.getReturnType(), node)
-        addModality(descriptor, node)
-        addVisibility(descriptor, node)
-
-        return node
-    }
-
-    override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.TypeParameter)
-        reference(data!!, node, DocumentationReference.Kind.Detail)
-        val builtIns = KotlinBuiltIns.getInstance()
-        for (constraint in descriptor.getUpperBounds()) {
-            if (constraint == builtIns.getDefaultBound())
-                continue
-            val constraintNode = DocumentationNode(descriptor, constraint.toString(), DocumentationContent.Empty, DocumentationNode.Kind.UpperBound)
-            reference(node, constraintNode, DocumentationReference.Kind.Detail)
-        }
-        for (constraint in descriptor.getLowerBounds()) {
-            if (builtIns.isNothing(constraint))
-                continue
-            val constraintNode = DocumentationNode(descriptor, constraint.toString(), DocumentationContent.Empty, DocumentationNode.Kind.LowerBound)
-            reference(node, constraintNode, DocumentationReference.Kind.Detail)
-        }
-        return node
-    }
-
-    override fun visitPropertyDescriptor(descriptor: PropertyDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Property)
-        reference(data!!, node, DocumentationReference.Kind.Member)
-
-        addType(descriptor, descriptor.getType(), node)
-        addModality(descriptor, node)
-        addVisibility(descriptor, node)
-        return node
-    }
-
-    override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val doc = context.getDocumentation(descriptor!!)
-        val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Constructor)
-        reference(data!!, node, DocumentationReference.Kind.Member)
-
-        addVisibility(descriptor, node)
-
-        return node
-    }
-
-    override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val node = DocumentationNode(descriptor!!, descriptor.getFqName().asString(), DocumentationContent.Empty, DocumentationNode.Kind.Package)
-        reference(data!!, node, DocumentationReference.Kind.Member)
-        return node
-    }
-
-    override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor?, data: DocumentationNode?): DocumentationNode? {
-        val node = DocumentationNode(descriptor!!, descriptor.fqName.asString(), DocumentationContent.Empty, DocumentationNode.Kind.Package)
-        reference(data!!, node, DocumentationReference.Kind.Member)
-        return node
-    }
-}
diff --git a/test/data/comments/explicitSummary.kt b/test/data/comments/explicitSummary.kt
new file mode 100644
index 0000000..68f75c9
--- /dev/null
+++ b/test/data/comments/explicitSummary.kt
@@ -0,0 +1,6 @@
+
+/**
+ * $$summary: Summary
+ * $$description: Description
+ */
+val property = "test"
\ No newline at end of file
diff --git a/test/data/comments/multilineDoc.kt b/test/data/comments/multilineDoc.kt
index 960c78b..31cfa3a 100644
--- a/test/data/comments/multilineDoc.kt
+++ b/test/data/comments/multilineDoc.kt
@@ -1,5 +1,6 @@
 /**
  * doc1
+ *
  * doc2
  * doc3
  */
diff --git a/test/src/TestAPI.kt b/test/src/TestAPI.kt
index 27d0271..309654e 100644
--- a/test/src/TestAPI.kt
+++ b/test/src/TestAPI.kt
@@ -38,3 +38,30 @@
     Disposer.dispose(environment)
 }
 
+fun StringBuilder.appendChildren(node: ContentNode) : StringBuilder {
+    for (child in node.children) {
+        val childText = child.toTestString()
+        append(childText)
+    }
+    return this
+}
+
+fun StringBuilder.appendNode(node: ContentNode) : StringBuilder {
+    when (node) {
+        is ContentText -> {
+            append(node.text)
+        }
+        is ContentEmphasis -> append("*").appendChildren(node).append("*")
+        else -> {
+            appendChildren(node)
+        }
+    }
+    return this
+}
+
+fun ContentNode.toTestString(): String {
+    val node = this
+    return StringBuilder {
+        appendNode(node)
+    }.toString()
+}
diff --git a/test/src/markdown/ParserTest.kt b/test/src/markdown/ParserTest.kt
index 726505c..2a310bb 100644
--- a/test/src/markdown/ParserTest.kt
+++ b/test/src/markdown/ParserTest.kt
@@ -3,13 +3,13 @@
 import org.junit.Test
 import org.jetbrains.dokka
 import org.jetbrains.dokka.MarkdownProcessor
-import org.jetbrains.dokka.dump
+import org.jetbrains.dokka.toTestString
 import org.jetbrains.dokka.toHtml
 
 public class ParserTest {
     fun runTestFor(text : String) {
-        val markdownTree = MarkdownProcessor().parse(text)
-        println(markdownTree.dump())
+        val markdownTree = MarkdownProcessor.parse(text)
+        println(markdownTree.toTestString())
         println(markdownTree.toHtml())
     }
 
@@ -79,5 +79,37 @@
     Test fun emph() {
         runTestFor("*text*")
     }
+
+    Test fun emphAndEmptySection() {
+        runTestFor("*text* \$sec:")
+    }
+
+    Test fun emphAndSection() {
+        runTestFor("*text* \$sec: some text")
+    }
+
+    Test fun emphAndBracedSection() {
+        runTestFor("Text *bold* text \${sec}: some text")
+    }
+
+    Test fun section() {
+        runTestFor(
+                "Plain text \$one: Summary \${two}: Description with *emphasis* \${An example of a section}: Example")
+    }
+
+    Test fun anonymousSection() {
+        runTestFor("Summary\n\nDescription\n")
+    }
+
+    Test fun specialSection() {
+        runTestFor(
+                "Plain text \$\$summary: Summary \${\$description}: Description \${\$An example of a section}: Example")
+    }
+
+    Test fun emptySection() {
+        runTestFor(
+                "Plain text \$summary:")
+    }
+
 }
 
diff --git a/test/src/markdown/Specification.kt b/test/src/markdown/Specification.kt
index e0cda02..ba1f836 100644
--- a/test/src/markdown/Specification.kt
+++ b/test/src/markdown/Specification.kt
@@ -4,7 +4,7 @@
 import org.jetbrains.kmark.test.*
 import org.jetbrains.dokka.*
 
-[RunWith(javaClass<MarkdownTestRunner>())]
+//[RunWith(javaClass<MarkdownTestRunner>())]
 class Specification : MarkdownSpecification("test/data/markdown/spec.txt", {
     markdownToHtml(it.replace("→", "\t"))
 })
\ No newline at end of file
diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt
index 05e156f..7068038 100644
--- a/test/src/model/ClassTest.kt
+++ b/test/src/model/ClassTest.kt
@@ -10,7 +10,7 @@
             with(model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Class, kind)
                 assertEquals("Klass", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertEquals("<init>", members.single().name)
                 assertTrue(links.none())
@@ -23,7 +23,7 @@
             with(model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Class, kind)
                 assertEquals("Klass", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertEquals("<init>", members.single().name)
                 assertTrue(links.none())
@@ -36,7 +36,7 @@
             with(model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Object, kind)
                 assertEquals("Obj", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -49,21 +49,21 @@
             with (model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Class, kind)
                 assertEquals("Klass", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertTrue(links.none())
 
                 assertEquals(1, members.count())
                 with(members.elementAt(0)) {
                     assertEquals("<init>", name)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(DocumentationNode.Kind.Constructor, kind)
                     assertEquals(2, details.count())
                     assertEquals("public", details.elementAt(0).name)
                     with(details.elementAt(1)) {
                         assertEquals("name", name)
                         assertEquals(DocumentationNode.Kind.Parameter, kind)
-                        assertEquals(DocumentationContent.Empty, doc)
+                        assertEquals(Content.Empty, doc)
                         assertEquals("String", details.single().name)
                         assertTrue(links.none())
                         assertTrue(members.none())
@@ -80,14 +80,14 @@
             with(model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Class, kind)
                 assertEquals("Klass", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertTrue(links.none())
 
                 assertEquals(2, members.count())
                 with(members.elementAt(0)) {
                     assertEquals("<init>", name)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(DocumentationNode.Kind.Constructor, kind)
                     assertEquals(1, details.count())
                     assertEquals("public", details.elementAt(0).name)
@@ -96,7 +96,7 @@
                 }
                 with(members.elementAt(1)) {
                     assertEquals("fn", name)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(DocumentationNode.Kind.Function, kind)
                     assertEquals("Unit", detail(DocumentationNode.Kind.Type).name)
                     assertTrue(links.none())
@@ -111,14 +111,14 @@
             with(model.members.single().members.single()) {
                 assertEquals(DocumentationNode.Kind.Class, kind)
                 assertEquals("Klass", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(2, details.count())
                 assertTrue(links.none())
 
                 assertEquals(2, members.count())
                 with(members.elementAt(0)) {
                     assertEquals("<init>", name)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(DocumentationNode.Kind.Constructor, kind)
                     assertEquals(1, details.count())
                     assertEquals("public", details.elementAt(0).name)
@@ -127,7 +127,7 @@
                 }
                 with(members.elementAt(1)) {
                     assertEquals("name", name)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(DocumentationNode.Kind.Property, kind)
                     assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                     assertTrue(members.none())
diff --git a/test/src/model/CommentTest.kt b/test/src/model/CommentTest.kt
index 28f717d..62f64a5 100644
--- a/test/src/model/CommentTest.kt
+++ b/test/src/model/CommentTest.kt
@@ -8,7 +8,7 @@
     Test fun emptyDoc() {
         verifyModel("test/data/comments/emptyDoc.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
             }
         }
     }
@@ -16,7 +16,7 @@
     Test fun emptyDocButComment() {
         verifyModel("test/data/comments/emptyDocButComment.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
             }
         }
     }
@@ -24,8 +24,8 @@
     Test fun multilineDoc() {
         verifyModel("test/data/comments/multilineDoc.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc1".toRichString(), doc.summary)
-                assertEquals("doc2\ndoc3".toRichString(), doc.description)
+                assertEquals("doc1", doc.summary.toTestString())
+                assertEquals("doc2\ndoc3\n", doc.description.toTestString())
             }
         }
     }
@@ -33,8 +33,8 @@
     Test fun multipleDocs() {
         verifyModel("test/data/comments/multipleDocs.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc1".toRichString(), doc.summary)
-                assertEquals("doc2\ndoc3".toRichString(), doc.description)
+                assertEquals("doc1", doc.summary.toTestString())
+                assertEquals("doc2\ndoc3", doc.description.toTestString())
             }
         }
     }
@@ -42,8 +42,8 @@
     Test fun multilineDocWithComment() {
         verifyModel("test/data/comments/multilineDocWithComment.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc1".toRichString(), doc.summary)
-                assertEquals("doc2\ndoc3".toRichString(), doc.description)
+                assertEquals("doc1", doc.summary.toTestString())
+                assertEquals("doc2\ndoc3", doc.description.toTestString())
             }
         }
     }
@@ -51,7 +51,7 @@
     Test fun oneLineDoc() {
         verifyModel("test/data/comments/oneLineDoc.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc".toRichString(), doc.summary)
+                assertEquals("doc", doc.summary.toTestString())
             }
         }
     }
@@ -59,7 +59,7 @@
     Test fun oneLineDocWithComment() {
         verifyModel("test/data/comments/oneLineDocWithComment.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc".toRichString(), doc.summary)
+                assertEquals("doc", doc.summary.toTestString())
             }
         }
     }
@@ -67,7 +67,7 @@
     Test fun oneLineDocWithEmptyLine() {
         verifyModel("test/data/comments/oneLineDocWithEmptyLine.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("doc".toRichString(), doc.summary)
+                assertEquals("doc", doc.summary.toTestString())
             }
         }
     }
@@ -76,24 +76,34 @@
         verifyModel("test/data/comments/emptySection.kt") { model ->
             with(model.members.single().members.single()) {
                 assertEquals(NormalStyle, NormalStyle)
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
-                    assertEquals(RichString.empty, text)
+                    assertEquals("", toTestString())
                 }
             }
         }
     }
 
+    Test fun explicitSummary() {
+        verifyModel("test/data/comments/explicitSummary.kt") { model ->
+            with(model.members.single().members.single()) {
+                assertEquals("Summary", doc.summary.toTestString())
+                assertEquals("Description", doc.description.toTestString())
+                assertEquals(2, doc.sections.count())
+            }
+        }
+    }
+
     Test fun section1() {
         verifyModel("test/data/comments/section1.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
-                    assertEquals("section one".toRichString(), text)
+                    assertEquals("section one", toTestString())
                 }
             }
         }
@@ -102,15 +112,15 @@
     Test fun section2() {
         verifyModel("test/data/comments/section2.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(3, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
-                    assertEquals("section one".toRichString(), text)
+                    assertEquals("section one", toTestString())
                 }
                 with (doc.sections["two"]!!) {
                     assertEquals("two", label)
-                    assertEquals("section two".toRichString(), text)
+                    assertEquals("section two", toTestString())
                 }
             }
         }
@@ -119,11 +129,11 @@
     Test fun sectionOnOneLine() {
         verifyModel("test/data/comments/sectionOnOneLine.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
-                    assertEquals("same line".toRichString(), text)
+                    assertEquals("same line", toTestString())
                 }
             }
         }
@@ -132,11 +142,11 @@
     Test fun emptySectionOnOneLine() {
         verifyModel("test/data/comments/emptySectionOnOneLine.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
-                    assertEquals(RichString.empty, text)
+                    assertEquals("", toTestString())
                 }
             }
         }
@@ -145,12 +155,12 @@
     Test fun multilineSection() {
         verifyModel("test/data/comments/multilineSection.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["one"]!!) {
                     assertEquals("one", label)
                     assertEquals("""line one
-line two""".toRichString(), text)
+line two""", toTestString())
                 }
             }
         }
@@ -159,11 +169,11 @@
     Test fun sectionWithBracedLabel() {
         verifyModel("test/data/comments/sectionWithBracedLabel.kt") { model ->
             with(model.members.single().members.single()) {
-                assertEquals("Summary".toRichString(), doc.summary)
+                assertEquals("Summary", doc.summary.toTestString())
                 assertEquals(2, doc.sections.count())
                 with (doc.sections["this.label.is.really.long"]!!) {
                     assertEquals("this.label.is.really.long", label)
-                    assertEquals("section one".toRichString(), text)
+                    assertEquals("section one", toTestString())
                 }
             }
         }
diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt
index f090fcd..fe8b639 100644
--- a/test/src/model/FunctionTest.kt
+++ b/test/src/model/FunctionTest.kt
@@ -10,7 +10,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("fn", name)
                 assertEquals(DocumentationNode.Kind.Function, kind)
-                assertEquals("Function fn".toRichString(), doc.summary)
+                assertEquals("Function fn", doc.summary.toTestString())
                 assertEquals("Unit", detail(DocumentationNode.Kind.Type).name)
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -23,7 +23,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("fn", name)
                 assertEquals(DocumentationNode.Kind.Function, kind)
-                assertEquals("Function with receiver".toRichString(), doc.summary)
+                assertEquals("Function with receiver", doc.summary.toTestString())
                 assertEquals("Unit", details.elementAt(0).name)
 
                 assertEquals(4, details.count())
@@ -32,7 +32,7 @@
                 with(details.elementAt(3)) {
                     assertEquals("<this>", name)
                     assertEquals(DocumentationNode.Kind.Receiver, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals("String", details.single().name)
                     assertTrue(members.none())
                     assertTrue(links.none())
@@ -48,7 +48,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("generic", name)
                 assertEquals(DocumentationNode.Kind.Function, kind)
-                assertEquals("generic function".toRichString(), doc.summary)
+                assertEquals("generic function", doc.summary.toTestString())
 
                 assertEquals(4, details.count())
                 assertEquals("Unit", details.elementAt(0).name)
@@ -57,7 +57,7 @@
                 with(details.elementAt(3)) {
                     assertEquals("T", name)
                     assertEquals(DocumentationNode.Kind.TypeParameter, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertTrue(details.none())
                     assertTrue(members.none())
                     assertTrue(links.none())
@@ -73,7 +73,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("generic", name)
                 assertEquals(DocumentationNode.Kind.Function, kind)
-                assertEquals("generic function".toRichString(), doc.summary)
+                assertEquals("generic function", doc.summary.toTestString())
 
                 assertEquals(5, details.count())
                 assertEquals("Unit", details.elementAt(0).name)
@@ -82,11 +82,11 @@
                 with(details.elementAt(3)) {
                     assertEquals("T", name)
                     assertEquals(DocumentationNode.Kind.TypeParameter, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     with(details.single()) {
                         assertEquals("R", name)
                         assertEquals(DocumentationNode.Kind.UpperBound, kind)
-                        assertEquals(DocumentationContent.Empty, doc)
+                        assertEquals(Content.Empty, doc)
                         assertTrue(details.none())
                         assertTrue(members.none())
                         assertTrue(links.none())
@@ -97,7 +97,7 @@
                 with(details.elementAt(4)) {
                     assertEquals("R", name)
                     assertEquals(DocumentationNode.Kind.TypeParameter, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertTrue(members.none())
                     assertTrue(links.none())
                 }
@@ -113,10 +113,9 @@
             with(model.members.single().members.single()) {
                 assertEquals("function", name)
                 assertEquals(DocumentationNode.Kind.Function, kind)
-                assertEquals("Multiline".toRichString(), doc.summary)
+                assertEquals("Multiline", doc.summary.toTestString())
                 assertEquals("""Function
-Documentation""".toRichString()
-                             , doc.description)
+Documentation""", doc.description.toTestString())
 
                 assertEquals(4, details.count())
                 assertEquals("Unit", details.elementAt(0).name)
@@ -125,7 +124,7 @@
                 with(details.elementAt(3)) {
                     assertEquals("x", name)
                     assertEquals(DocumentationNode.Kind.Parameter, kind)
-                    assertEquals("parameter".toRichString(), doc.summary)
+                    assertEquals("parameter", doc.summary.toTestString())
                     assertEquals("Int", details.single().name)
                     assertTrue(members.none())
                     assertTrue(links.none())
diff --git a/test/src/model/PackageTest.kt b/test/src/model/PackageTest.kt
index b855ad7..848fff3 100644
--- a/test/src/model/PackageTest.kt
+++ b/test/src/model/PackageTest.kt
@@ -10,7 +10,7 @@
             with(model.members.single()) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
                 assertEquals("", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -23,7 +23,7 @@
             with(model.members.single()) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
                 assertEquals("simple", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -36,7 +36,7 @@
             with(model.members.single()) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
                 assertEquals("dot.name", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -50,14 +50,14 @@
             with(model.members.single { it.name == "simple" }) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
                 assertEquals("simple", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
             }
             with(model.members.single { it.name == "dot.name" }) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -71,7 +71,7 @@
             with(model.members.elementAt(0)) {
                 assertEquals(DocumentationNode.Kind.Package, kind)
                 assertEquals("simple", name)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertTrue(details.none())
                 assertTrue(members.none())
                 assertTrue(links.none())
diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt
index 07651ce..ed49bd6 100644
--- a/test/src/model/PropertyTest.kt
+++ b/test/src/model/PropertyTest.kt
@@ -10,7 +10,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("property", name)
                 assertEquals(DocumentationNode.Kind.Property, kind)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -23,7 +23,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("property", name)
                 assertEquals(DocumentationNode.Kind.Property, kind)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                 assertTrue(members.none())
                 assertTrue(links.none())
@@ -36,13 +36,13 @@
             with(model.members.single().members.single()) {
                 assertEquals("property", name)
                 assertEquals(DocumentationNode.Kind.Property, kind)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                 assertTrue(links.none())
                 with(members.single()) {
                     assertEquals("<get-property>", name)
                     assertEquals(DocumentationNode.Kind.Function, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                     assertTrue(links.none())
                     assertTrue(members.none())
@@ -56,7 +56,7 @@
             with(model.members.single().members.single()) {
                 assertEquals("property", name)
                 assertEquals(DocumentationNode.Kind.Property, kind)
-                assertEquals(DocumentationContent.Empty, doc)
+                assertEquals(Content.Empty, doc)
                 assertEquals(3, details.count())
                 with(details.elementAt(0)) {
                     assertEquals(DocumentationNode.Kind.Type, kind)
@@ -76,7 +76,7 @@
                 with(members.elementAt(0)) {
                     assertEquals("<get-property>", name)
                     assertEquals(DocumentationNode.Kind.Function, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                     assertTrue(links.none())
                     assertTrue(members.none())
@@ -84,7 +84,7 @@
                 with(members.elementAt(1)) {
                     assertEquals("<set-property>", name)
                     assertEquals(DocumentationNode.Kind.Function, kind)
-                    assertEquals(DocumentationContent.Empty, doc)
+                    assertEquals(Content.Empty, doc)
                     assertEquals(4, details.count())
                     assertEquals("Unit", details.elementAt(0).name)
                     assertEquals("final", details.elementAt(1).name)
@@ -92,7 +92,7 @@
                     with(details.elementAt(3)) {
                         assertEquals("value", name)
                         assertEquals(DocumentationNode.Kind.Parameter, kind)
-                        assertEquals(DocumentationContent.Empty, doc)
+                        assertEquals(Content.Empty, doc)
                         assertEquals("String", detail(DocumentationNode.Kind.Type).name)
                         assertTrue(links.none())
                         assertTrue(members.none())