Markdown: render pages under /+doc/

Parse and render any file ending with ".md" as markdown using the
pegdown processor.  This allows a repository to be used as a
documentation source.

If "navbar.md" is found in the top level directory it is rendered as a
horizontal bar across the top of the page.  A navbar.md is an outline:

  * [Home](/index.md)
  * [APIs](/api/index.md)
  * [Source](/src/main/java/index.md)

Any links starting with "/" and ending in ".md" are resolved as
relative to the top of the Git repository.

As an extension to Markdown the special block [TOC] is replaced with
a table of contents from the headers of the text.

Markdown support can be optionally disabled by setting the config
variable markdown.render to false.

Change-Id: Ic111628c59cfadfdca37bf0cc637ee8a14d54c37
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java
index bad8ab7..a3feb6c 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gitiles.doc.MarkdownHelper.getInnerText;
 
+import com.google.gitiles.GitilesView;
 import com.google.gitiles.doc.html.HtmlBuilder;
 import com.google.template.soy.data.SanitizedContent;
 import com.google.template.soy.shared.restricted.EscapingConventions;
@@ -69,8 +70,13 @@
   private final ReferenceMap references = new ReferenceMap();
   private final HtmlBuilder html = new HtmlBuilder();
   private final TocFormatter toc = new TocFormatter(html, 3);
+  private final GitilesView view;
   private TableState table;
 
+  public MarkdownToHtml(GitilesView view) {
+    this.view = view;
+  }
+
   /** Render the document AST to sanitized HTML. */
   public SanitizedContent toSoyHtml(RootNode node) {
     if (node == null) {
@@ -180,7 +186,7 @@
   @Override
   public void visit(AutoLinkNode node) {
     String url = node.getText();
-    html.open("a").attribute("href", url)
+    html.open("a").attribute("href", href(url))
         .appendAndEscape(url)
         .close("a");
   }
@@ -197,7 +203,7 @@
   public void visit(WikiLinkNode node) {
     String text = node.getText();
     String path = text.replace(' ', '-') + ".md";
-    html.open("a").attribute("href", path)
+    html.open("a").attribute("href", href(path))
         .appendAndEscape(text)
         .close("a");
   }
@@ -205,7 +211,7 @@
   @Override
   public void visit(ExpLinkNode node) {
     html.open("a")
-        .attribute("href", node.url)
+        .attribute("href", href(node.url))
         .attribute("title", node.title);
     visitChildren(node);
     html.close("a");
@@ -216,7 +222,7 @@
     ReferenceNode ref = references.get(node.referenceKey, getInnerText(node));
     if (ref != null) {
       html.open("a")
-          .attribute("href", ref.getUrl())
+          .attribute("href", href(ref.getUrl()))
           .attribute("title", ref.getTitle());
       visitChildren(node);
       html.close("a");
@@ -226,6 +232,13 @@
     }
   }
 
+  private String href(String url) {
+    if (MarkdownHelper.isAbsolutePathToMarkdown(url)) {
+      return GitilesView.doc().copyFrom(view).setPathPart(url).build().toUrl();
+    }
+    return url;
+  }
+
   @Override
   public void visit(ExpImageNode node) {
     html.open("img")