diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-13 15:30:55 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-13 15:30:55 +0200 |
commit | 418792e491764cc0e11e86522d43835a3da82fa6 (patch) | |
tree | 779116bc14395673e4029da48321ea9bc9e58a08 /cli/context.cxx | |
parent | 124341e83bb9e8959508fac8c798506f82f21938 (diff) |
Add support for man formatting
Diffstat (limited to 'cli/context.cxx')
-rw-r--r-- | cli/context.cxx | 154 |
1 files changed, 129 insertions, 25 deletions
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 += "<pre>"; + 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 += "</pre>"; - } - else - { - if (!first || b.para) - { - if (ot == ot_html) - v += "<p>"; + 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 += "<pre>"; + v.append (l, n); + v += "</pre>"; + } + else + { + if (!first || b.para) v += "<p>"; + format_line (ot, v, l, n); + if (!first || b.para) v += "</p>"; + } - if (!first || b.para) + break; + } + case ot_man: { - if (ot == ot_html) - v += "</p>"; + 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:: |