Improve wiki formatting for preformatted text and lists

We now format a paragraph that contains at least one line with
manual indentation as a pre-formatted text block.  This fixes
the case presented by dbort in GERRIT-134:

==
key_passwords[None] = None # comment explaining why
for k in sorted(keylist):
 if not k:
   continue
 p = subprocess...
==

The entire block is now preformatted.

We also now format paragraphs that contain lines starting with "- "
or "* " as a list, with each line being a unique item in that list.

Bug: GERRIT-134
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtml.java b/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtml.java
index 1e4da6e..a14a8c4 100644
--- a/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtml.java
+++ b/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtml.java
@@ -66,10 +66,49 @@
    * </pre>
    */
   public SafeHtml wikify() {
-    SafeHtml s = linkify();
-    s = s.replaceAll("(^|\n)([ \t][^\n]*)", "$1<span class=\"gwtexpui-SafeHtml-WikiPreFormat\">$2</span><br />");
-    s = s.replaceAll("\n\n", "\n<p />\n");
-    return s;
+    final SafeHtmlBuilder r = new SafeHtmlBuilder();
+    for (final String p : linkify().asString().split("\n\n")) {
+      if (isPreFormat(p)) {
+        r.openElement("p");
+        for (final String line : p.split("\n")) {
+          r.openSpan();
+          r.setStyleName("gwtexpui-SafeHtml-WikiPreFormat");
+          r.append(new SafeHtmlString(line));
+          r.closeSpan();
+          r.br();
+        }
+        r.closeElement("p");
+        
+      } else if (isList(p)) {
+        r.openElement("ul");
+        r.setStyleName("gwtexpui-SafeHtml-WikiList");
+        for (String line : p.split("\n")) {
+          if (line.startsWith("-") || line.startsWith("*")) {
+            line = line.substring(1).trim();
+          }
+          r.openElement("li");
+          r.append(new SafeHtmlString(line));
+          r.closeElement("li");
+        }
+        r.closeElement("ul");
+        
+      } else {
+        r.openElement("p");
+        r.append(new SafeHtmlString(p));
+        r.closeElement("p");
+      }
+    }
+    return r.toSafeHtml();
+  }
+
+  private static boolean isPreFormat(final String p) {
+    return p.contains("\n ") || p.contains("\n\t") || p.startsWith(" ")
+        || p.startsWith("\t");
+  }
+
+  private static boolean isList(final String p) {
+    return p.contains("\n- ") || p.contains("\n* ") || p.startsWith("- ")
+        || p.startsWith("* ");
   }
 
   /**