From a89ee85a4b94a57e996a233c9d21679d1e1217a9 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 9 Feb 2016 13:33:09 +0200 Subject: Add support for variable expansion (\$var$) in doc strings --- cli/context.cxx | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++------ cli/context.hxx | 9 ++++- cli/html.cxx | 12 +++++-- cli/man.cxx | 6 ++-- cli/source.cxx | 8 ++--- cli/txt.cxx | 5 ++- 6 files changed, 125 insertions(+), 22 deletions(-) diff --git a/cli/context.cxx b/cli/context.cxx index ea84f33..9ba20ab 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -618,24 +618,29 @@ format_line (output_type ot, string& r, const char* s, size_t n) throw generation_failed (); } - // Extract the link target. + // Find the end of the link target. // - for (++i; i + 1 < n; ++i) + size_t b (++i + 1), e (b); + for (; i + 1 < n; ++i) { char c (s[i + 1]); - if (c == ' ') + if (c == ' ' || c == '}') { - for (++i; i + 1 < n && s[i + 1] == ' '; ++i) ; // Skip spaces. - break; - } + e = i + 1; - if (c == '}') - break; + if (c == ' ') // Skip spaces. + for (++i; i + 1 < n && s[i + 1] == ' '; ++i) ; - t += c; + break; + } } + // Run the link target through format_line(ot_plain) to handle + // escaping (e.g., \\$). + // + format_line (ot_plain, t, s + b, e - b); + if (t.empty ()) { cerr << "error: missing link target in documentation paragraph '" @@ -1115,7 +1120,7 @@ html_margin (string& v) string context:: -format (output_type ot, string const& s, bool para) +format (semantics::scope& scope, output_type ot, string const& s, bool para) { stack blocks; blocks.push (block (block::text, para)); // Top-level. @@ -1137,6 +1142,7 @@ format (output_type ot, string const& s, bool para) const char* l; size_t n; + string subst; // Variable-substituted. if (pre) { @@ -1158,6 +1164,14 @@ format (output_type ot, string const& s, bool para) l = s.c_str () + b; n = (last ? s.size () : e) - b; + + // Perform variable expansions (\$var$). + // + if (substitute (scope, l, n, subst)) + { + l = subst.c_str (); + n = subst.size (); + } } const char* ol (l); // Original, full line for diagnostics. @@ -1840,6 +1854,79 @@ substitute (const string& s, semantics::cli_unit& u, const path* d) return r; } +bool context:: +substitute (semantics::scope& scope, const char* s, size_t n, string& result) +{ + bool sub (false); + result.clear (); + + // Scan the string looking for variables (\$var$). + // + size_t b (0), e (b); + for (char p ('\0'); e != n; ++e) + { + char c (s[e]); + + if (p == '\\') + { + if (c == '\\') // Escape sequence. + { + p = '\0'; + continue; + } + + if (c == '$') + { + // Variable expansion. + // + sub = true; + + // Find the closing '$'. + // + size_t p (e + 1); // Position of the second '$'. + for (; p != n && s[p] != '$'; ++p) ; + + if (p == n) + { + cerr << "error: missing closing '$' in '" << string (s, n) << "'" + << endl; + throw generation_failed (); + } + + result.append (s, b, e - b - 1); // Save what came before. + + // Var name. + // + ++e; + string v (s, e, p - e); + + // Lookup and substiute. + // + using semantics::doc; + + if (doc* d = unit.lookup (scope.fq_name (), "var: " + v)) + result += d->front (); + else + { + cerr << "error: undefined variable '" << v << "' in '" + << string (s, n) << "'" << endl; + throw generation_failed (); + } + + e = p; + b = e + 1; + } + } + + p = s[e]; + } + + if (sub) + result.append (s, b, e - b); // Last chunk. + + return sub; +} + string context:: fq_name (semantics::nameable& n, bool cxx_name) { diff --git a/cli/context.hxx b/cli/context.hxx index b33bfef..9f10a00 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -127,7 +127,7 @@ public: // If para is true, start a new paragraph. // string - format (output_type, string const&, bool para); + format (semantics::scope&, output_type, string const&, bool para); void format_line (output_type, string&, const char*, size_t); @@ -146,6 +146,13 @@ public: return substitute (s, unit, p); } + // Substitute doc variable expansions (\$var$). Note that it leaves escapes + // (\\$) as is. Return true if any substitutions have been made, in which + // case result will contain the expansion result. + // + bool + substitute (semantics::scope&, const char* s, size_t n, string& result); + public: static string const& ename (semantics::nameable& n) diff --git a/cli/html.cxx b/cli/html.cxx index 7f68453..da94a65 100644 --- a/cli/html.cxx +++ b/cli/html.cxx @@ -139,7 +139,10 @@ namespace if (n > 1) translate_arg (ds[0], arg_set); - string s (format (ot_html, escape_html (translate (d, arg_set)), true)); + string s (format (ds.scope (), + ot_html, + escape_html (translate (d, arg_set)), + true)); if (s.empty ()) return; @@ -208,7 +211,7 @@ namespace translate_arg ( doc.size () > 0 ? doc[0] : string (""), arg_set)); - os << ' ' << format (ot_html, escape_html (s), false); + os << ' ' << format (o.scope (), ot_html, escape_html (s), false); } os << "" << endl; @@ -231,7 +234,10 @@ namespace // Format the documentation string. // - d = format (ot_html, escape_html (translate (d, arg_set)), false); + d = format (o.scope (), + ot_html, + escape_html (translate (d, arg_set)), + false); wrap_lines (os, "
" + d + "
", 4); os << endl; diff --git a/cli/man.cxx b/cli/man.cxx index b7bce6f..7e2757c 100644 --- a/cli/man.cxx +++ b/cli/man.cxx @@ -94,7 +94,7 @@ namespace if (n > 1) translate_arg (ds[0], arg_set); - string s (format (ot_man, translate (d, arg_set), true)); + string s (format (ds.scope (), ot_man, translate (d, arg_set), true)); if (s.empty ()) return; @@ -145,7 +145,7 @@ namespace translate_arg ( doc.size () > 0 ? doc[0] : string (""), arg_set)); - os << ' ' << format (ot_man, s, false); + os << ' ' << format (o.scope (), ot_man, s, false); } os << "\"" << endl; @@ -168,7 +168,7 @@ namespace // Format the documentation string. // - d = format (ot_man, translate (d, arg_set), false); + d = format (o.scope (), ot_man, translate (d, arg_set), false); wrap_lines (os, d); os << endl; diff --git a/cli/source.cxx b/cli/source.cxx index f8d367e..14a6a9a 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -260,7 +260,7 @@ namespace if (n > 1 && options.ansi_color ()) translate_arg (ds[0], arg_set); - d = format (ot_plain, translate (d, arg_set), true); + d = format (ds.scope (), ot_plain, translate (d, arg_set), true); if (d.empty ()) return; @@ -331,7 +331,7 @@ namespace s = translate_arg (s, arg_set); } - l += txt_size (format (ot_plain, s, false)); + l += txt_size (format (o.scope (), ot_plain, s, false)); } if (l > length_) @@ -422,7 +422,7 @@ namespace if (color) s = translate_arg (s, arg_set); - s = format (ot_plain, s, false); + s = format (o.scope (), ot_plain, s, false); os << escape_str (s); l += txt_size (s); @@ -458,7 +458,7 @@ namespace if (color) d = translate (d, arg_set); - d = format (ot_plain, d, false); + d = format (o.scope (), ot_plain, d, false); if (!d.empty ()) wrap_lines (os, d, length_ + 1, l); // +1 for extra space after arg. diff --git a/cli/txt.cxx b/cli/txt.cxx index c537c55..ccce8ef 100644 --- a/cli/txt.cxx +++ b/cli/txt.cxx @@ -156,7 +156,10 @@ namespace if (n > 1 && options.ansi_color ()) translate_arg (ds[0], arg_set); - string s (format (ot_plain, translate (d, arg_set), true)); + string s (format (ds.scope (), + ot_plain, + translate (d, arg_set), + true)); if (s.empty ()) return; -- cgit v1.1