Escape < and > in non-code markdown output.

Bug: gn:8
Change-Id: I0689922ffc7677913d0afb64c6c15c476c04f358
Reviewed-on: https://gn-review.googlesource.com/2703
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index cb9632b..d73a94f 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -150,7 +150,7 @@
 
 ## <a name="commands"></a>Commands
 
-### <a name="analyze"></a>**gn analyze <out_dir> <input_path> <output_path>**
+### <a name="analyze"></a>**gn analyze &lt;out_dir&gt; &lt;input_path&gt; &lt;output_path&gt;**
 
 ```
   Analyze which targets are affected by a list of files.
@@ -220,7 +220,7 @@
   tries really hard to always write something to the output JSON and convey
   errors that way rather than via return codes.
 ```
-### <a name="args"></a>**gn args <out_dir> [\--list] [\--short] [\--args] [\--overrides-only]**
+### <a name="args"></a>**gn args &lt;out_dir&gt; [\--list] [\--short] [\--args] [\--overrides-only]**
 
 ```
   See also "gn help buildargs" for a more high-level overview of how
@@ -305,7 +305,7 @@
     given arguments set (which may affect the values of other
     arguments).
 ```
-### <a name="check"></a>**gn check <out_dir> [<label_pattern>] [\--force]**
+### <a name="check"></a>**gn check &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--force]**
 
 ```
   GN's include header checker validates that the includes for C-like source
@@ -420,13 +420,13 @@
   gn check out/Default "//foo/*
       Check only the files in targets in the //foo directory tree.
 ```
-### <a name="clean"></a>**gn clean <out_dir>**
+### <a name="clean"></a>**gn clean &lt;out_dir&gt;**
 
 ```
   Deletes the contents of the output directory except for args.gn and
   creates a Ninja build environment sufficient to regenerate the build.
 ```
-### <a name="desc"></a>**gn desc <out_dir> <label or pattern> [<what to show>] [\--blame] "**
+### <a name="desc"></a>**gn desc &lt;out_dir&gt; &lt;label or pattern&gt; [&lt;what to show&gt;] [\--blame] "**
 #### **[\--format=json]**
 
 ```
@@ -438,7 +438,7 @@
   targets.
 ```
 
-#### **Possibilities for <what to show>**
+#### **Possibilities for &lt;what to show&gt;**
 
 ```
   (If unspecified an overall summary will be displayed.)
@@ -585,7 +585,7 @@
       Shows defines set for the //base:base target, annotated by where
       each one was set from.
 ```
-### <a name="format"></a>**gn format [\--dump-tree] (\--stdin | <build_file>)**
+### <a name="format"></a>**gn format [\--dump-tree] (\--stdin | &lt;build_file&gt;)**
 
 ```
   Formats .gn file to a standard format.
@@ -628,7 +628,7 @@
   gn format /abspath/some/BUILD.gn
   gn format --stdin
 ```
-### <a name="gen"></a>**gn gen [\--check] [<ide options>] <out_dir>**
+### <a name="gen"></a>**gn gen [\--check] [&lt;ide options&gt;] &lt;out_dir&gt;**
 
 ```
   Generates ninja files from the current tree and puts them in the given output
@@ -758,7 +758,7 @@
       used for various Clang-based tooling, allowing for the replay of individual
       compilations independent of the build system.
 ```
-### <a name="help"></a>**gn help <anything>**
+### <a name="help"></a>**gn help &lt;anything&gt;**
 
 ```
   Yo dawg, I heard you like help on your help so I put help on the help in the
@@ -780,7 +780,7 @@
   gn help --markdown all
       Dump all help to stdout in markdown format.
 ```
-### <a name="ls"></a>**gn ls <out_dir> [<label_pattern>] [\--all-toolchains] [\--as=...]**
+### <a name="ls"></a>**gn ls &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--all-toolchains] [\--as=...]**
 ```
       [--type=...] [--testonly=...]
 
@@ -853,7 +853,7 @@
       Lists all variants of the target //base:base (it may be referenced
       in multiple toolchains).
 ```
-### <a name="path"></a>**gn path <out_dir> <target_one> <target_two>**
+### <a name="path"></a>**gn path &lt;out_dir&gt; &lt;target_one&gt; &lt;target_two&gt;**
 
 ```
   Finds paths of dependencies between two targets. Each unique path will be
@@ -898,7 +898,7 @@
 ```
   gn path out/Default //base //tools/gn
 ```
-### <a name="refs"></a>**gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)***
+### <a name="refs"></a>**gn refs &lt;out_dir&gt; (&lt;label_pattern&gt;|&lt;label&gt;|&lt;file&gt;|@&lt;response_file&gt;)***
 ```
         [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
 
diff --git a/tools/gn/command_help.cc b/tools/gn/command_help.cc
index cff2052..6e816ce 100644
--- a/tools/gn/command_help.cc
+++ b/tools/gn/command_help.cc
@@ -120,20 +120,26 @@
 
   OutputString("\n");
 
-  if (is_markdown)
-    OutputString("## <a name=\"commands\"></a>Commands\n\n");
+  if (is_markdown) {
+    OutputString("## <a name=\"commands\"></a>Commands\n\n", DECORATION_NONE,
+                 NO_ESCAPING);
+  }
   for (const auto& c : commands::GetCommands())
     PrintLongHelp(c.second.help);
 
-  if (is_markdown)
-    OutputString("## <a name=\"targets\"></a>Target declarations\n\n");
+  if (is_markdown) {
+    OutputString("## <a name=\"targets\"></a>Target declarations\n\n",
+                 DECORATION_NONE, NO_ESCAPING);
+  }
   for (const auto& f : functions::GetFunctions()) {
     if (f.second.is_target)
       PrintLongHelp(f.second.help);
   }
 
-  if (is_markdown)
-    OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n");
+  if (is_markdown) {
+    OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n",
+                 DECORATION_NONE, NO_ESCAPING);
+  }
   for (const auto& f : functions::GetFunctions()) {
     if (!f.second.is_target)
       PrintLongHelp(f.second.help);
@@ -142,7 +148,8 @@
   if (is_markdown) {
     OutputString(
         "## <a name=\"predefined_variables\"></a>"
-        "Built-in predefined variables\n\n");
+        "Built-in predefined variables\n\n",
+        DECORATION_NONE, NO_ESCAPING);
   }
   for (const auto& v : variables::GetBuiltinVariables())
     PrintLongHelp(v.second.help);
@@ -150,13 +157,16 @@
   if (is_markdown) {
     OutputString(
         "## <a name=\"target_variables\"></a>"
-        "Variables you set in targets\n\n");
+        "Variables you set in targets\n\n",
+        DECORATION_NONE, NO_ESCAPING);
   }
   for (const auto& v : variables::GetTargetVariables())
     PrintLongHelp(v.second.help);
 
-  if (is_markdown)
-    OutputString("## <a name=\"other\"></a>Other help topics\n\n");
+  if (is_markdown) {
+    OutputString("## <a name=\"other\"></a>Other help topics\n\n",
+                 DECORATION_NONE, NO_ESCAPING);
+  }
   PrintLongHelp(kBuildArgs_Help, "buildargs");
   PrintLongHelp(kDotfile_Help, "dotfile");
   PrintLongHelp(kExecution_Help, "execution");
@@ -170,8 +180,10 @@
   PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
   PrintLongHelp(kSourceExpansion_Help, "source_expansion");
 
-  if (is_markdown)
-    OutputString("## <a name=\"switches\"></a>Command Line Switches\n\n");
+  if (is_markdown) {
+    OutputString("## <a name=\"switches\"></a>Command Line Switches\n\n",
+                 DECORATION_NONE, NO_ESCAPING);
+  }
   PrintSwitchHelp();
 }
 
diff --git a/tools/gn/standard_out.cc b/tools/gn/standard_out.cc
index 6dd6a6a..d2ff52e 100644
--- a/tools/gn/standard_out.cc
+++ b/tools/gn/standard_out.cc
@@ -35,6 +35,9 @@
 
 bool is_markdown = false;
 
+// True while output is going into a markdown ```...``` code block.
+bool in_body = false;
+
 void EnsureInitialized() {
   if (initialized)
     return;
@@ -96,7 +99,9 @@
 
 #if defined(OS_WIN)
 
-void OutputString(const std::string& output, TextDecoration dec) {
+void OutputString(const std::string& output,
+                  TextDecoration dec,
+                  HtmlEscaping escaping) {
   EnsureInitialized();
   DWORD written = 0;
 
@@ -135,6 +140,12 @@
     // at least escape the instances where this shows up in a heading.
     base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
   }
+  if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+    // Markdown auto-escapes < and > in code sections (and converts &lt; to
+    // &amp;tl; there), but not elsewhere.
+    base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+    base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+  }
   ::WriteFile(hstdout, tmpstr.c_str(), static_cast<DWORD>(tmpstr.size()),
               &written, nullptr);
 
@@ -147,7 +158,9 @@
 
 #else
 
-void OutputString(const std::string& output, TextDecoration dec) {
+void OutputString(const std::string& output,
+                  TextDecoration dec,
+                  HtmlEscaping escaping) {
   EnsureInitialized();
   if (is_markdown) {
     OutputMarkdownDec(dec);
@@ -181,6 +194,12 @@
     // at least escape the instances where this shows up in a heading.
     base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
   }
+  if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+    // Markdown auto-escapes < and > in code sections (and converts &lt; to
+    // &amp;tl; there), but not elsewhere.
+    base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+    base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+  }
   WriteToStdOut(tmpstr.data());
 
   if (is_markdown) {
@@ -248,7 +267,7 @@
   EnsureInitialized();
 
   bool first_header = true;
-  bool in_body = false;
+  in_body = false;
   std::size_t empty_lines = 0;
   for (const std::string& line : base::SplitString(
            text, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
@@ -279,7 +298,8 @@
               the_tag = line.substr(0, line.find(':'));
             }
           }
-          OutputString("### <a name=\"" + the_tag + "\"></a>", DECORATION_NONE);
+          OutputString("### <a name=\"" + the_tag + "\"></a>", DECORATION_NONE,
+                       NO_ESCAPING);
           first_header = false;
         } else {
           OutputString("#### ", DECORATION_NONE);
diff --git a/tools/gn/standard_out.h b/tools/gn/standard_out.h
index f2bb733..c793b29 100644
--- a/tools/gn/standard_out.h
+++ b/tools/gn/standard_out.h
@@ -16,8 +16,17 @@
   DECORATION_YELLOW
 };
 
+enum HtmlEscaping {
+  NO_ESCAPING,
+
+  // Convert < and > to &lt; and &gt; when writing markdown output in non-code
+  // sections.
+  DEFAULT_ESCAPING,
+};
+
 void OutputString(const std::string& output,
-                  TextDecoration dec = DECORATION_NONE);
+                  TextDecoration dec = DECORATION_NONE,
+                  HtmlEscaping = DEFAULT_ESCAPING);
 
 // If printing markdown, this generates table-of-contents entries with
 // links to the actual help; otherwise, prints a one-line description.