summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-11-18 15:35:45 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-11-18 15:35:45 +0200
commit6280033ac4d3d3646c4c512a1c852c9c8f088f80 (patch)
tree29d61def865015dca6f1f5f820f0ae7de5904bf8
parent6dc69580d6101fb1adb1733dad11b5f5ab5fc831 (diff)
Add support for ANSI colorization of usage output
-rw-r--r--cli/context.cxx40
-rw-r--r--cli/options.cli9
-rw-r--r--cli/options.cxx12
-rw-r--r--cli/options.hxx4
-rw-r--r--cli/options.ixx6
-rw-r--r--cli/source.cxx87
-rw-r--r--doc/cli.16
-rw-r--r--doc/cli.xhtml7
8 files changed, 148 insertions, 23 deletions
diff --git a/cli/context.cxx b/cli/context.cxx
index d41bb3c..7459406 100644
--- a/cli/context.cxx
+++ b/cli/context.cxx
@@ -614,8 +614,23 @@ format_line (output_type ot, string& r, const char* s, size_t n)
{
case ot_plain:
{
- if (b & code)
- r += "'";
+ if ((b & link) == 0)
+ {
+ if (options.ansi_color ())
+ {
+ if (b & bold)
+ r += "\033[1m";
+
+ if (b & itlc)
+ r += "\033[4m";
+ }
+ else
+ {
+ if (b & code)
+ r += "'";
+ }
+ }
+
break;
}
case ot_html:
@@ -713,8 +728,25 @@ format_line (output_type ot, string& r, const char* s, size_t n)
}
else
{
- if (b & code)
- r += "'";
+ if (options.ansi_color ())
+ {
+ // While there are codes to turn off bold (22) and
+ // underline (24), it is not clear how widely they
+ // are supported.
+ //
+ r += "\033[0m"; // Clear all.
+
+ if (eb & bold)
+ r += "\033[1m";
+
+ if (eb & itlc)
+ r += "\033[4m";
+ }
+ else
+ {
+ if (b & code)
+ r += "'";
+ }
}
break;
diff --git a/cli/options.cli b/cli/options.cli
index 841081d..4972ba9 100644
--- a/cli/options.cli
+++ b/cli/options.cli
@@ -139,6 +139,15 @@ class options
files, and would like their usage to have the same indentation level."
};
+ bool --ansi-color
+ {
+ "Use ANSI color escape sequences when printing usage. By \"color\" we
+ really only mean the bold and underline modifiers. Note that Windows
+ console does not recognize ANSI escape sequences and will display
+ them as garbage. However, if you pipe such output through \c{\b{less}(1)},
+ it will display them correctly."
+ };
+
bool --exclude-base
{
"Exclude base class information from usage and documentation."
diff --git a/cli/options.cxx b/cli/options.cxx
index c225d49..d6b0e84 100644
--- a/cli/options.cxx
+++ b/cli/options.cxx
@@ -506,7 +506,6 @@ namespace cli
const_cast<char*> (o), 0
};
-
if (!kstr.empty ())
{
av[1] = const_cast<char*> (kstr.c_str ());
@@ -565,6 +564,7 @@ options ()
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -630,6 +630,7 @@ options (int& argc,
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -698,6 +699,7 @@ options (int start,
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -766,6 +768,7 @@ options (int& argc,
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -836,6 +839,7 @@ options (int start,
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -902,6 +906,7 @@ options (::cli::scanner& s,
long_usage_ (),
short_usage_ (),
option_length_ (0),
+ ansi_color_ (),
exclude_base_ (),
class__ (),
docvar_ (),
@@ -1000,6 +1005,9 @@ print_usage (::std::ostream& os)
os << "--option-length <len> Indent option descriptions <len> characters when" << ::std::endl
<< " printing usage." << ::std::endl;
+ os << "--ansi-color Use ANSI color escape sequences when printing" << ::std::endl
+ << " usage." << ::std::endl;
+
os << "--exclude-base Exclude base class information from usage and" << ::std::endl
<< " documentation." << ::std::endl;
@@ -1164,6 +1172,8 @@ struct _cli_options_map_init
&::cli::thunk< options, bool, &options::short_usage_ >;
_cli_options_map_["--option-length"] =
&::cli::thunk< options, std::size_t, &options::option_length_ >;
+ _cli_options_map_["--ansi-color"] =
+ &::cli::thunk< options, bool, &options::ansi_color_ >;
_cli_options_map_["--exclude-base"] =
&::cli::thunk< options, bool, &options::exclude_base_ >;
_cli_options_map_["--class"] =
diff --git a/cli/options.hxx b/cli/options.hxx
index ecafb22..933a41a 100644
--- a/cli/options.hxx
+++ b/cli/options.hxx
@@ -426,6 +426,9 @@ class options
option_length () const;
const bool&
+ ansi_color () const;
+
+ const bool&
exclude_base () const;
const std::vector<std::string>&
@@ -569,6 +572,7 @@ class options
bool long_usage_;
bool short_usage_;
std::size_t option_length_;
+ bool ansi_color_;
bool exclude_base_;
std::vector<std::string> class__;
std::map<std::string, std::string> docvar_;
diff --git a/cli/options.ixx b/cli/options.ixx
index b146a60..c4a3220 100644
--- a/cli/options.ixx
+++ b/cli/options.ixx
@@ -330,6 +330,12 @@ option_length () const
}
inline const bool& options::
+ansi_color () const
+{
+ return this->ansi_color_;
+}
+
+inline const bool& options::
exclude_base () const
{
return this->exclude_base_;
diff --git a/cli/source.cxx b/cli/source.cxx
index 971c639..d58f46b 100644
--- a/cli/source.cxx
+++ b/cli/source.cxx
@@ -7,7 +7,7 @@
#include "source.hxx"
-using std::cerr;
+using namespace std;
namespace
{
@@ -171,8 +171,36 @@ namespace
}
};
+
+ // Return the number of "text characters", ignoring any escape sequences
+ // (e.g., ANSI color).
//
- //
+ static size_t
+ text_size (const string& s, size_t p = 0, size_t n = string::npos)
+ {
+ size_t r (0);
+
+ n = n == string::npos ? s.size () : n + p;
+
+ // The start position (p) might be pointing half-way into the
+ // escape sequence. So we always have to scan from the start.
+ //
+ for (size_t i (0), m (s.size ()); i < n; ++i)
+ {
+ if (s[i] == '\033') // ANSI escape: "\033[Nm"
+ {
+ i += 3;
+ assert (i < m && s[i] == 'm');
+ continue;
+ }
+
+ if (i >= p)
+ ++r;
+ }
+
+ return r;
+ }
+
struct option_length: traversal::option, context
{
option_length (context& c, size_t& l, type*& o)
@@ -190,6 +218,8 @@ namespace
if (options.suppress_undocumented () && doc.empty ())
return;
+ bool color (options.ansi_color ());
+
size_t l (0);
names& n (o.named ());
@@ -207,10 +237,15 @@ namespace
{
l++; // ' ' seperator
- if (doc.size () > 0)
- l += format (ot_plain, doc[0], false).size ();
- else
- l += 5; // <arg>
+ string s (doc.size () > 0 ? doc[0] : string ("<arg>"));
+
+ if (color)
+ {
+ std::set<string> arg_set;
+ s = translate_arg (s, arg_set);
+ }
+
+ l += text_size (format (ot_plain, s, false));
}
if (l > length_)
@@ -242,6 +277,8 @@ namespace
if (options.suppress_undocumented () && doc.empty ())
return;
+ bool color (options.ansi_color ());
+
size_t l (0);
names& n (o.named ());
@@ -255,28 +292,34 @@ namespace
l++;
}
+ if (color)
+ os << "\\033[1m"; // Bold.
+
os << escape_str (*i);
+
+ if (color)
+ os << "\\033[0m";
+
l += i->size ();
}
string type (o.type ().name ());
+ std::set<string> arg_set;
if (type != "bool" || doc.size () >= 3)
{
os << ' ';
l++;
- if (doc.size () > 0)
- {
- string s (format (ot_plain, doc[0], false));
- os << escape_str (s);
- l += s.size ();
- }
- else
- {
- os << "<arg>";
- l += 5;
- }
+ string s (doc.size () > 0 ? doc[0] : string ("<arg>"));
+
+ if (color)
+ s = translate_arg (s, arg_set);
+
+ s = format (ot_plain, s, false);
+
+ os << escape_str (s);
+ l += text_size (s);
}
// Figure out which documentation string we should use.
@@ -306,6 +349,9 @@ namespace
// Format the documentation string.
//
+ if (color)
+ d = translate (d, arg_set);
+
d = format (ot_plain, d, false);
if (!d.empty ())
@@ -323,7 +369,7 @@ namespace
// we get the same output on Windows (which has two characters for
// a newline).
//
- if (d[i] == '\n' || i - b == 78 - length_)
+ if (d[i] == '\n' || text_size (d, b, i - b) == 78 - length_)
{
if (b != 0) // Not a first line.
{
@@ -423,6 +469,11 @@ namespace
r += "\\\"";
break;
}
+ case '\033':
+ {
+ r += "\\033";
+ break;
+ }
default:
{
r += s[i];
diff --git a/doc/cli.1 b/doc/cli.1
index 88f6be0..663fdaf 100644
--- a/doc/cli.1
+++ b/doc/cli.1
@@ -120,6 +120,12 @@ provided\.
Indent option descriptions \fIlen\fR characters when printing usage\. This is
useful when you have multiple options classes, potentially in separate files,
and would like their usage to have the same indentation level\.
+.IP "\fB--ansi-color\fR"
+Use ANSI color escape sequences when printing usage\. By "color" we really
+only mean the bold and underline modifiers\. Note that Windows console does
+not recognize ANSI escape sequences and will display them as garbage\.
+However, if you pipe such output through \fBless\fR(1)\fR, it will display
+them correctly\.
.IP "\fB--exclude-base\fR"
Exclude base class information from usage and documentation\.
.IP "\fB--class\fR \fIfq-name\fR"
diff --git a/doc/cli.xhtml b/doc/cli.xhtml
index e2baa6b..305087c 100644
--- a/doc/cli.xhtml
+++ b/doc/cli.xhtml
@@ -165,6 +165,13 @@
separate files, and would like their usage to have the same indentation
level.</dd>
+ <dt><code><b>--ansi-color</b></code></dt>
+ <dd>Use ANSI color escape sequences when printing usage. By "color" we
+ really only mean the bold and underline modifiers. Note that Windows
+ console does not recognize ANSI escape sequences and will display them as
+ garbage. However, if you pipe such output through
+ <code><b>less</b>(1)</code>, it will display them correctly.</dd>
+
<dt><code><b>--exclude-base</b></code></dt>
<dd>Exclude base class information from usage and documentation.</dd>