From b8b194ea5fa6ba7a3aac7ff212eb62ccfb942dbf Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 24 Nov 2015 14:30:02 +0200 Subject: Implement --class-doc option --- cli/context.cxx | 51 ++++++++++++++++++++++++++++++++++++++++++++++ cli/context.hxx | 17 ++++++++++++++++ cli/html.cxx | 63 ++++++++++++++++++++++++++++++++++----------------------- cli/man.cxx | 59 +++++++++++++++++++++++++++++++---------------------- cli/options.cli | 20 +++++++++++++++--- cli/options.cxx | 22 ++++++++++++++++++-- cli/options.hxx | 8 ++++++++ cli/options.ixx | 12 +++++++++++ cli/source.cxx | 39 +++++++++++++++++------------------ 9 files changed, 217 insertions(+), 74 deletions(-) (limited to 'cli') diff --git a/cli/context.cxx b/cli/context.cxx index 86de357..b3aebc8 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -1594,6 +1594,57 @@ ns_close (const string& qn, bool last) } while (true); } +class_doc context:: +class_doc (semantics::class_& c) +{ + typedef map map; + const map& m (options.class_doc ()); + + string n (c.fq_name ()); + + map::const_iterator i (m.find (n)); + + if (i == m.end ()) + { + n = string (n, 2, string::npos); // Try without leading '::'. + i = m.find (n); + } + + if (i == m.end ()) + return cd_default; + + const string& k (i->second); + + if (k == "exclude") + return cd_exclude; + else if (k == "short") + return cd_short; + else if (k == "long") + return cd_long; + else + { + cerr << "error: unknown --class-doc kind value '" << k << "'" << endl; + throw generation_failed (); + } +} + +string context:: +first_sentence (string const& s) +{ + size_t p (s.find ('.')); + + // Add some heuristics here: check that there is a space + // (or end of text) after the period. + // + while (p != string::npos && + p + 1 <= s.size () && + s[p + 1] != ' ' && + s[p + 1] != '\n') + p = s.find ('.', p + 1); + + return p == string::npos ? s : string (s, 0, p + 1); +} + // namespace // diff --git a/cli/context.hxx b/cli/context.hxx index 460287c..f16f8c2 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -30,6 +30,14 @@ enum usage ut_both }; +enum class_doc +{ + cd_default, + cd_exclude, + cd_short, + cd_long +}; + class context { public: @@ -38,6 +46,7 @@ public: typedef ::options options_type; typedef ::usage usage_type; + typedef ::class_doc class_doc_type; private: struct data; @@ -153,6 +162,14 @@ public: ns_close (const string& name, bool last = true); public: + class_doc_type + class_doc (semantics::class_&); + +public: + string + first_sentence (string const&); + +public: context (std::ostream&, semantics::cli_unit&, options_type const&); context (context&); diff --git a/cli/html.cxx b/cli/html.cxx index 6659bf6..7f68453 100644 --- a/cli/html.cxx +++ b/cli/html.cxx @@ -114,7 +114,8 @@ namespace struct doc: traversal::doc, context { - doc (context& c, bool& l): context (c), list_ (l) {} + doc (context& c, class_doc_type cd, bool& l) + : context (c), cd_ (cd), list_ (l) {} virtual void traverse (type& ds) @@ -124,10 +125,15 @@ namespace // n = 1 - common doc string // n = 2 - arg string, common doc string - // n > 2 - arg string, usage string, man string + // n > 2 - arg string, short string, long string // size_t n (ds.size ()); - const string& d (n == 1 ? ds[0] : n == 2 ? ds[1] : ds[2]); + 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) @@ -151,12 +157,14 @@ namespace } private: + class_doc_type cd_; bool& list_; // True if we are currently in
. }; struct option: traversal::option, context { - option (context& c, bool& l): context (c), list_ (l) {} + option (context& c, class_doc_type cd, bool& l) + : context (c), cd_ (cd), list_ (l) {} virtual void traverse (type& o) @@ -206,23 +214,19 @@ namespace os << "" << endl; string d; - - // If we have both the long and the short descriptions, use - // the long one. - // if (type == "bool" && doc.size () < 3) { if (doc.size () > 1) - d = doc[1]; + d = doc[cd_ == cd_short ? 0 : 1]; else if (doc.size () > 0) - d = doc[0]; + d = (cd_ == cd_short ? first_sentence (doc[0]) : doc[0]); } else { if (doc.size () > 2) - d = doc[2]; + d = doc[cd_ == cd_short ? 1 : 2]; else if (doc.size () > 1) - d = doc[1]; + d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); } // Format the documentation string. @@ -234,6 +238,7 @@ namespace } private: + class_doc_type cd_; bool& list_; // True if we are currently in
. }; @@ -241,19 +246,36 @@ namespace // struct class_: traversal::class_, context { - class_ (context& c): context (c) {} + 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); - names (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_; }; } @@ -265,8 +287,8 @@ generate_html (context& ctx) traversal::cli_unit unit; traversal::names unit_names; traversal::namespace_ ns; - doc dc (ctx, list); - class_ cl (ctx); + doc dc (ctx, cd_default, list); + class_ cl (ctx, list); unit >> unit_names; unit_names >> dc; unit_names >> ns; @@ -278,15 +300,6 @@ generate_html (context& ctx) ns_names >> ns; ns_names >> cl; - traversal::inherits cl_inherits; - cl >> cl_inherits >> cl; - - option op (ctx, list); - traversal::names cl_names; - cl >> cl_names; - cl_names >> dc; - cl_names >> op; - if (ctx.options.class_ ().empty ()) unit.dispatch (ctx.unit); else diff --git a/cli/man.cxx b/cli/man.cxx index 606fec6..b7bce6f 100644 --- a/cli/man.cxx +++ b/cli/man.cxx @@ -70,7 +70,7 @@ namespace struct doc: traversal::doc, context { - doc (context& c): context (c) {} + doc (context& c, class_doc_type cd): context (c), cd_ (cd) {} virtual void traverse (type& ds) @@ -80,10 +80,15 @@ namespace // n = 1 - common doc string // n = 2 - arg string, common doc string - // n > 2 - arg string, usage string, man string + // n > 2 - arg string, short string, long string // size_t n (ds.size ()); - const string& d (n == 1 ? ds[0] : n == 2 ? ds[1] : ds[2]); + 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) @@ -97,11 +102,14 @@ namespace wrap_lines (os, s); os << endl; } + + private: + class_doc_type cd_; }; struct option: traversal::option, context { - option (context& c) : context (c) {} + option (context& c, class_doc_type cd) : context (c), cd_ (cd) {} virtual void traverse (type& o) @@ -143,23 +151,19 @@ namespace os << "\"" << endl; string d; - - // If we have both the long and the short descriptions, use - // the long one. - // if (type == "bool" && doc.size () < 3) { if (doc.size () > 1) - d = doc[1]; + d = doc[cd_ == cd_short ? 0 : 1]; else if (doc.size () > 0) - d = doc[0]; + d = (cd_ == cd_short ? first_sentence (doc[0]) : doc[0]); } else { if (doc.size () > 2) - d = doc[2]; + d = doc[cd_ == cd_short ? 1 : 2]; else if (doc.size () > 1) - d = doc[1]; + d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); } // Format the documentation string. @@ -169,25 +173,41 @@ namespace wrap_lines (os, d); os << endl; } + + private: + class_doc_type cd_; }; // // struct class_: traversal::class_, context { - class_ (context& c): context (c) {} + class_ (context& c): context (c) {*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); - names (c); + doc dc (*this, cd); + option op (*this, cd); + traversal::names n; + n >> dc; + n >> op; + names (c, n); if (!options.exclude_base () && options.include_base_last ()) inherits (c); } + + private: + traversal::inherits inherits_; }; } @@ -197,7 +217,7 @@ generate_man (context& ctx) traversal::cli_unit unit; traversal::names unit_names; traversal::namespace_ ns; - doc dc (ctx); + doc dc (ctx, cd_default); class_ cl (ctx); unit >> unit_names; unit_names >> ns; @@ -210,15 +230,6 @@ generate_man (context& ctx) ns_names >> dc; ns_names >> cl; - traversal::inherits cl_inherits; - cl >> cl_inherits >> cl; - - option op (ctx); - traversal::names cl_names; - cl >> cl_names; - cl_names >> dc; - cl_names >> op; - if (ctx.options.class_ ().empty ()) unit.dispatch (ctx.unit); else diff --git a/cli/options.cli b/cli/options.cli index 9212024..9554d10 100644 --- a/cli/options.cli +++ b/cli/options.cli @@ -182,11 +182,25 @@ class options By default, base classes are included first." }; + std::map --class-doc + { + "=", + "Specify the documentation that should be used for the options + class . The value should be a fully-qualified class name, + for example, \cb{app::options}. The value can be \cb{short}, + \cb{long}, or \cb{exclude}. If the value is \cb{exclude}, then the + class documentation is excluded from usage and man/HTML output. For + usage, the \cb{short} and \cb{long} values determine which usage + function will be called when the class is used as base or as part of + the page usage (see \cb{--page-usage}). For man/HTML, these values + determine which documentation strings are used in the output." + }; + std::vector --class { - "", - "Generate the man page or HTML documentation only for the options - class. The name should be a fully-qualified options class name, + "", + "Generate the man page or HTML documentation only for the options class + . The value should be a fully-qualified options class name, for example, \cb{app::options}. To generate documentation for multiple classes, repeat this option and the documentation will be produced in the order specified. This functionality is useful if you need to assemble diff --git a/cli/options.cxx b/cli/options.cxx index 6869a60..95f2a6c 100644 --- a/cli/options.cxx +++ b/cli/options.cxx @@ -592,6 +592,8 @@ options () ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -699,6 +701,8 @@ options (int& argc, ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -809,6 +813,8 @@ options (int start, ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -919,6 +925,8 @@ options (int& argc, ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -1031,6 +1039,8 @@ options (int start, ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -1139,6 +1149,8 @@ options (::cli::scanner& s, ansi_color_ (), exclude_base_ (), include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), class__ (), class__specified_ (false), docvar_ (), @@ -1286,8 +1298,11 @@ print_usage (::std::ostream& os, ::cli::usage_para p) os << "--include-base-last Include base class information after derived for" << ::std::endl << " usage and documentation." << ::std::endl; - os << "--class Generate the man page or HTML documentation only" << ::std::endl - << " for the options class." << ::std::endl; + os << "--class-doc = Specify the documentation that should be" << ::std::endl + << " used for the options class ." << ::std::endl; + + os << "--class Generate the man page or HTML documentation only" << ::std::endl + << " for the options class ." << ::std::endl; os << "--docvar|-v = Set documentation variable to the value" << ::std::endl << " ." << ::std::endl; @@ -1467,6 +1482,9 @@ struct _cli_options_map_init &::cli::thunk< options, bool, &options::exclude_base_ >; _cli_options_map_["--include-base-last"] = &::cli::thunk< options, bool, &options::include_base_last_ >; + _cli_options_map_["--class-doc"] = + &::cli::thunk< options, std::map, &options::class_doc_, + &options::class_doc_specified_ >; _cli_options_map_["--class"] = &::cli::thunk< options, std::vector, &options::class__, &options::class__specified_ >; diff --git a/cli/options.hxx b/cli/options.hxx index 0612882..268e0fd 100644 --- a/cli/options.hxx +++ b/cli/options.hxx @@ -484,6 +484,12 @@ class options const bool& include_base_last () const; + const std::map& + class_doc () const; + + bool + class_doc_specified () const; + const std::vector& class_ () const; @@ -735,6 +741,8 @@ class options bool ansi_color_; bool exclude_base_; bool include_base_last_; + std::map class_doc_; + bool class_doc_specified_; std::vector class__; bool class__specified_; std::map docvar_; diff --git a/cli/options.ixx b/cli/options.ixx index 24dc37b..3adfbd0 100644 --- a/cli/options.ixx +++ b/cli/options.ixx @@ -397,6 +397,18 @@ include_base_last () const return this->include_base_last_; } +inline const std::map& options:: +class_doc () const +{ + return this->class_doc_; +} + +inline bool options:: +class_doc_specified () const +{ + return this->class_doc_specified_; +} + inline const std::vector& options:: class_ () const { diff --git a/cli/source.cxx b/cli/source.cxx index 2cb317d..7bf5e8d 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -533,24 +533,6 @@ namespace } private: - string - first_sentence (string const& s) - { - size_t p (s.find ('.')); - - // Add some heuristics here: check that there is a space - // (or end of text) after the period. - // - while (p != string::npos && - p + 1 <= s.size () && - s[p + 1] != ' ' && - s[p + 1] != '\n') - p = s.find ('.', p + 1); - - return p == string::npos ? s : string (s, 0, p + 1); - } - - private: size_t length_; usage_type usage_; paragraph& para_; @@ -596,7 +578,15 @@ namespace virtual void traverse (type& c) { - const char* t (usage != ut_both || usage_ == ut_short ? "" : "long_"); + class_doc_type cd (class_doc (c)); + + if (cd == cd_exclude) + return; + + const char* t ( + (cd == cd_default + ? usage != ut_both || usage_ == ut_short + : cd == cd_short) ? "" : "long_"); os << "// " << escape (c.name ()) << " base" << endl << "//" << endl @@ -1098,12 +1088,21 @@ namespace virtual void traverse (type& c) { + class_doc_type cd (class_doc (c)); + + if (cd == cd_exclude) + return; + + const char* t ( + (cd == cd_default + ? usage != ut_both || usage_ == ut_short + : cd == cd_short) ? "" : "long_"); + string p ( para_ == para_unknown ? "p" : cli + "::usage_para::" + (para_ == para_text ? "text" : "option")); - const char* t (usage != ut_both || usage_ == ut_short ? "" : "long_"); os << "p = " << fq_name (c) << "::print_" << t << "usage (os, " << p << ");" << endl; -- cgit v1.1