// file : cli/html.cxx // author : Boris Kolpackov // copyright : Copyright (c) 2009-2017 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file #include #include #include using namespace std; namespace { static string escape_html (string const& s) { string r; r.reserve (s.size ()); for (size_t i (0), n (s.size ()); i < n; ++i) { switch (s[i]) { case '<': { r += "<"; break; } case '&': { r += "&"; break; } default: { r += s[i]; break; } } } return r; } static void wrap_lines (ostream& os, const string& d, size_t indent) { size_t lim (78 - indent); string ind (indent, ' '); bool nl (true); // True if last written to os character is a newline. size_t b (0), e (0), i (0); for (size_t n (d.size ()); i < n; ++i) { // First handle
.
      //
      if (d.compare (i, 5, "
") == 0)
      {
        // Write what might have already accumulated.
        //
        if (b != i)
        {
          if (nl)
            os << ind;

          os << string (d, b, i - b);
          nl = false;
        }

        // Output everything until (and including) closing 
as is. // e = d.find ("
", i + 5); assert (e != string::npos); e += 6; // Now points past '>'. if (nl) os << ind; os << string (d, i, e - i); b = e; i = e - 1; // For ++i in loop header. nl = false; continue; } if (d[i] == ' ' || d[i] == '\n') e = i; if (d[i] == '\n' || (i - b >= lim && e != b)) { if (nl && b != e) os << ind; os << string (d, b, e - b) << endl; b = e = e + 1; nl = true; } } // Write the last line. // if (b != i) { if (nl) os << ind; os << string (d, b, i - b); } } struct doc: traversal::doc, context { doc (context& c, class_doc_type cd, bool& l) : context (c), cd_ (cd), list_ (l) {} virtual void traverse (type& ds) { if (ds.name ().compare (0, 3, "doc") != 0) // Ignore doc variables. return; // n = 1 - common doc string // n = 2 - arg string, common doc string // n > 2 - arg string, short string, long string // size_t n (ds.size ()); const string& d ( n == 1 ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) : (n == 2 ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) : ds[cd_ == cd_short ? 1 : 2])); std::set arg_set; if (n > 1) translate_arg (ds[0], arg_set); unsigned short t (toc); // Detect the switch to/from TOC mode. string s ( format (ds.scope (), escape_html (translate (d, arg_set)), true)); if (s.empty ()) return; if (list_) { os << " " << endl << endl; list_ = false; } wrap_lines (os, s, (t || toc) ? 0 : 2); // TOC mode does its own thing. if (!toc) // TOC mode does its own thing. os << endl << endl; } private: class_doc_type cd_; bool& list_; // True if we are currently in
. }; struct option: traversal::option, context { option (context& c, class_doc_type cd, bool& l) : context (c), cd_ (cd), list_ (l) {} virtual void traverse (type& o) { using semantics::names; semantics::doc_strings const& doc (o.doc ()); if (options.suppress_undocumented () && doc.empty ()) return; if (toc) return; // No option documentation in the TOC mode. if (!list_) { os << "
" << endl; list_ = true; } else os << endl; // Separate from the previous
. names& n (o.named ()); os << "
"; for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) { if (i != n.name_begin ()) os << "|"; os << escape_html (*i); } os << ""; string type (o.type ().name ()); std::set arg_set; if (type != "bool" || doc.size () >= 3) { string s ( translate_arg ( doc.size () > 0 ? doc[0] : string (""), arg_set)); os << ' ' << format (o.scope (), escape_html (s), false); } os << "
" << endl; string d; if (type == "bool" && doc.size () < 3) { if (doc.size () > 1) d = doc[cd_ == cd_short ? 0 : 1]; else if (doc.size () > 0) d = (cd_ == cd_short ? first_sentence (doc[0]) : doc[0]); } else { if (doc.size () > 2) d = doc[cd_ == cd_short ? 1 : 2]; else if (doc.size () > 1) d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); } // Format the documentation string. // d = format (o.scope (), escape_html (translate (d, arg_set)), false); wrap_lines (os, "
" + d + "
", 4); os << endl; } private: class_doc_type cd_; bool& list_; // True if we are currently in
. }; // // struct class_: traversal::class_, context { class_ (context& c, bool& l): context (c), list_ (l) { *this >> inherits_ >> *this; } virtual void traverse (type& c) { class_doc_type cd (class_doc (c)); if (cd == cd_exclude) return; if (!options.exclude_base () && !options.include_base_last ()) inherits (c); doc dc (*this, cd, list_); option op (*this, cd, list_); traversal::names n; n >> dc; n >> op; names (c, n); if (!options.exclude_base () && options.include_base_last ()) inherits (c); } private: bool& list_; traversal::inherits inherits_; }; } void generate_html (context& ctx) { bool list (false); traversal::cli_unit unit; traversal::names unit_names; traversal::namespace_ ns; doc dc (ctx, cd_default, list); class_ cl (ctx, list); unit >> unit_names; unit_names >> dc; unit_names >> ns; unit_names >> cl; traversal::names ns_names; ns >> ns_names; ns_names >> dc; ns_names >> ns; ns_names >> cl; if (ctx.options.class_ ().empty ()) unit.dispatch (ctx.unit); else { for (vector::const_iterator i (ctx.options.class_ ().begin ()); i != ctx.options.class_ ().end (); ++i) { string n (*i); // Strip leading :: if present. // if (n.size () > 2 && n[0] == ':' && n[1] == ':') n = string (n, 2, string::npos); if (semantics::class_* c = ctx.unit.lookup ("", n)) cl.traverse (*c); else { cerr << "error: class '" << *i << "' not found" << endl; throw generation_failed (); } } } if (list) ctx.os << "
" << endl << endl; }