From 418792e491764cc0e11e86522d43835a3da82fa6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 13 Nov 2015 15:30:55 +0200 Subject: Add support for man formatting --- cli/context.cxx | 154 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 129 insertions(+), 25 deletions(-) (limited to 'cli/context.cxx') diff --git a/cli/context.cxx b/cli/context.cxx index 4b41132..d07b60b 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -641,8 +641,10 @@ struct block kind_type kind; bool para; // True if first text fragment should be in own paragraph. - string header; // Term in dl's li. + + string header; string value; + string trailer; block (kind_type k, bool p, const string& h = "") : kind (k), para (p), header (h) {} @@ -927,35 +929,86 @@ format (output_type ot, string const& s, bool para) string& v (b.value); bool first (v.empty ()); - // Separate paragraphs with a blank line. - // - if (!first) - v += "\n\n"; - - if (k == block::pre) + switch (ot) { - if (ot == ot_html) - v += "
";
+      case ot_plain:
+        {
+          // Separate paragraphs with a blank line.
+          //
+          if (!first)
+            v += "\n\n";
 
-        v.append (l, n);
+          if (k == block::pre)
+            v.append (l, n);
+          else
+            format_line (ot, v, l, n);
 
-        if (ot == ot_html)
-          v += "
"; - } - else - { - if (!first || b.para) - { - if (ot == ot_html) - v += "

"; + break; } + case ot_html: + { + // Separate paragraphs with a blank line. + // + if (!first) + v += "\n\n"; - format_line (ot, v, l, n); + if (k == block::pre) + { + v += "

";
+            v.append (l, n);
+            v += "
"; + } + else + { + if (!first || b.para) v += "

"; + format_line (ot, v, l, n); + if (!first || b.para) v += "

"; + } - if (!first || b.para) + break; + } + case ot_man: { - if (ot == ot_html) - v += "

"; + if (b.para) + { + if (!first) + v += "\n"; + + v += ".PP\n"; + } + else + { + if (!first) + v += "\n\n"; + } + + if (k == block::pre) + { + v += ".nf\n"; + + // Note that if we have several consequtive blank lines, they + // will be collapsed into a single one. No, .sp doesn't work. + // + char c, p ('\n'); // Current and previous characters. + for (size_t i (0); i != n; p = c, ++i) + { + switch (c = l[i]) + { + case '\\': v += '\\'; break; + case '.': v += p != '\n' ? "\\" : "\\&\\"; break; + } + + v += c; + } + + v += "\n.fi"; + } + else + { + format_line (ot, v, l, n); + } + + break; } } } @@ -1043,7 +1096,51 @@ format (output_type ot, string const& s, bool para) } case ot_man: { - break; // @@ TODO + // Seeing that we always write a macro, one newline is enough. + // + if (!v.empty ()) + v += "\n"; + + switch (pb.kind) + { + case block::h: v += ".SH \"" + pv + "\""; break; + case block::ul: + case block::ol: + case block::dl: + { + if (!b.para) // First list inside .IP. + { + // .IP within .IP? Just shoot me in the head already! We + // have to manually indent it with .RS/.RE *and* everything + // that comes after it (since .PP resets the indent). Why + // not just indent the whole list content? Because then the + // first line will never start on the same line as the term. + // + v += ".RS\n"; + b.trailer = "\n.RE"; + b.para = true; // Start emitting .PP from now on. + } + + v += pv; + break; + } + case block::li: + { + switch (b.kind) + { + case block::ul: v += ".IP \\(bu 2em\n" + pv; break; + case block::ol: v += ".IP " + ph + ". 4em\n" + pv; break; + case block::dl: v += ".IP \"" + ph + "\"\n" + pv; break; + default: break; + } + + break; + } + case block::text: + case block::pre: assert (false); + } + + break; } } } @@ -1058,7 +1155,14 @@ format (output_type ot, string const& s, bool para) throw generation_failed (); } - return blocks.top ().value; + block& b (blocks.top ()); + + switch (ot) + { + case ot_plain: + case ot_html: return b.value; + case ot_man: return b.value + b.trailer; + } } string context:: -- cgit v1.1