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 +++++++++++++++++------------------ doc/cli.1 | 18 +++++++++++++---- doc/cli.xhtml | 31 +++++++++++++++++++--------- 11 files changed, 253 insertions(+), 87 deletions(-) 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; diff --git a/doc/cli.1 b/doc/cli.1 index 1a89aed..57fd6c2 100644 --- a/doc/cli.1 +++ b/doc/cli.1 @@ -149,10 +149,20 @@ Exclude base class information from usage and documentation\. .IP "\fB--include-base-last\fR" Include base class information after derived for usage and documentation\. By default, base classes are included first\. -.IP "\fB--class\fR \fIfq-name\fR" -Generate the man page or HTML documentation only for the \fIfq-name\fR options -class\. The \fIfq-name\fR name should be a fully-qualified options class name, -for example, \fBapp::options\fR\. To generate documentation for multiple +.IP "\fB--class-doc\fR \fIname\fR=\fIkind\fR" +Specify the documentation \fIkind\fR that should be used for the options class +\fIname\fR\. The \fIname\fR value should be a fully-qualified class name, for +example, \fBapp::options\fR\. The \fIkind\fR value can be \fBshort\fR, +\fBlong\fR, or \fBexclude\fR\. If the value is \fBexclude\fR, then the class +documentation is excluded from usage and man/HTML output\. For usage, the +\fBshort\fR and \fBlong\fR values determine which usage function will be +called when the class is used as base or as part of the page usage (see +\fB--page-usage\fR)\. For man/HTML, these values determine which documentation +strings are used in the output\. +.IP "\fB--class\fR \fIname\fR" +Generate the man page or HTML documentation only for the options class +\fIname\fR\. The \fIname\fR value should be a fully-qualified options class +name, for example, \fBapp::options\fR\. 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 documentation from multiple classes in a specific order or to insert custom diff --git a/doc/cli.xhtml b/doc/cli.xhtml index 4d80b83..962d53b 100644 --- a/doc/cli.xhtml +++ b/doc/cli.xhtml @@ -197,15 +197,28 @@
Include base class information after derived for usage and documentation. By default, base classes are included first.
-
--class fq-name
-
Generate the man page or HTML documentation only for the - fq-name options class. The fq-name name should be a - fully-qualified options class name, for example, - 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 - documentation from multiple classes in a specific order or to insert - custom documentation between options belonging to different classes.
+
--class-doc name=kind
+
Specify the documentation kind that should be used for the + options class name. The name value should be a + fully-qualified class name, for example, app::options. + The kind value can be short, + long, or exclude. If the value is + exclude, then the class documentation is excluded from + usage and man/HTML output. For usage, the short and + long values determine which usage function will be + called when the class is used as base or as part of the page usage (see + --page-usage). For man/HTML, these values determine + which documentation strings are used in the output.
+ +
--class name
+
Generate the man page or HTML documentation only for the options class + name. The name value should be a fully-qualified options + class name, for example, 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 documentation from multiple classes in a + specific order or to insert custom documentation between options belonging + to different classes.
--docvar|-v name=val
Set documentation variable name to the value val. -- cgit v1.1