summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-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
6 files changed, 135 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];