From 20d84ff8ad4b443da0cc1ff067fb92ebc3d436a5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 9 Nov 2015 16:02:04 +0200 Subject: Implement support pre-formatted fragments For example: / x y z / Other text. --- cli/context.cxx | 48 ++++++++++++++++++++-------- cli/html.cxx | 48 +++++++++++++++++++++++++--- cli/parser.cxx | 98 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 164 insertions(+), 30 deletions(-) diff --git a/cli/context.cxx b/cli/context.cxx index c462a5e..165ecc2 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -662,19 +662,45 @@ format (output_type ot, string const& s, bool para) { string r; - // Iterate over lines (paragraphs). + // Iterate over lines (paragraphs) or pre-formatted sections. // - for (size_t b (0), e (s.find ('\n')); b != e;) + for (size_t b (0), e; ; b = e + 1) { - bool first (b == 0), last (e == string::npos); + bool pre (s[b] == 0x02); + bool first (b == 0), last; + + if (pre) + { + e = s.find (0x03, ++b); + assert (e != string::npos); + last = (e + 1 == s.size ()); + } + else + { + e = s.find ('\n', b); + last = (e == string::npos); + } const char* l (s.c_str () + b); - size_t n ((last ? s.size () : e) - b); + size_t n ((e != string::npos ? e : s.size ()) - b); + + if (pre) + { + ++e; // Skip newline that follows 0x03. + + if (ot == ot_html) + r += "
";
 
+      r.append (l, n);
+
+      if (ot == ot_html)
+        r += "
"; + } + // // Some paragraph blocks are only valid if we are required to start // a new paragraph (para is true). // - if (n >= 3 && strncmp (l, "\\h|", 3) == 0) + else if (n >= 3 && strncmp (l, "\\h|", 3) == 0) { if (!para) { @@ -721,16 +747,12 @@ format (output_type ot, string const& s, bool para) } } - // Separate paragraphs with newline. - // - if (!last) - r += '\n'; + if (last) + break; - // Get next line. + // Separate paragraphs with newline. // - b = e; - if (!last) - e = s.find ('\n', ++b); + r += "\n\n"; } return r; diff --git a/cli/html.cxx b/cli/html.cxx index 1f84fdd..e91fdd7 100644 --- a/cli/html.cxx +++ b/cli/html.cxx @@ -53,26 +53,66 @@ namespace size_t b (0), e (0), i (0); + bool nl (true); // True if last written to os character is a newline. + for (size_t n (d.size ()); i < n; ++i) { + // First handle
.
+      //
+      if (d.compare (i, 5, "
") == 0)
+      {
+        // Write what might have already accumulated.
+        //
+        if (b != i)
+        {
+          if (nl)
+            os << ind;
+
+          os << string (d, b, i - b);
+          nl = false;
+        }
+
+        // Output everything until (and including) closing 
as is. + // + e = d.find ("
", i + 5); + assert (e != string::npos); + e += 6; // Now points past '>'. + + if (nl) + os << ind; + + os << string (d, i, e - i); + + b = e; + i = e - 1; // For ++i in loop header. + nl = false; + continue; + } + if (d[i] == ' ' || d[i] == '\n') e = i; if (d[i] == '\n' || (i - b >= lim && e != b)) { - os << (b != 0 ? "\n" : "") << ind << string (d, b, e - b); + if (nl && b != e) + os << ind; - if (d[i] == '\n') - os << endl; + os << string (d, b, e - b) << endl; b = e = e + 1; + nl = true; } } // Write the last line. // if (b != i) - os << (b != 0 ? "\n" : "") << ind << string (d, b, i - b); + { + if (nl) + os << ind; + + os << string (d, b, i - b); + } } struct doc: traversal::doc, context diff --git a/cli/parser.cxx b/cli/parser.cxx index c7695bc..faac290 100644 --- a/cli/parser.cxx +++ b/cli/parser.cxx @@ -948,7 +948,7 @@ doc_string (const char* l, size_t n) { // Get rid of '"'. // - string t1, t2; + string t1, t2, t3; char p ('\0'); for (size_t i (0); i < n; ++i) @@ -967,13 +967,17 @@ doc_string (const char* l, size_t n) t1 += l[i]; } - // Get rid of leading and trailing spaces in each line. + // Get rid of leading and trailing spaces in each line. Also handle + // pre-formatted fragments. // if (t1.size () != 0) { bool more (true); size_t b (0), e, p; + bool pre (false); + size_t m; // Number of leading spaces to remove in pre. + while (more) { p = e = t1.find ('\n', b); @@ -984,15 +988,35 @@ doc_string (const char* l, size_t n) more = false; } - while (b < e && (t1[b] == 0x20 || t1[b] == 0x0D || t1[b] == 0x09)) - ++b; + // In the pre mode we only remove up to m leading whitespaces. + // + { + size_t i (0); + while (b < e && + (t1[b] == 0x20 || t1[b] == 0x0D || t1[b] == 0x09) && + (!pre || i != m)) + { + ++b; + ++i; + } - --e; + if (!pre) + m = i; + } + --e; while (e > b && (t1[e] == 0x20 || t1[e] == 0x0D || t1[e] == 0x09)) --e; - if (b <= e) + if (b == e && t1[b] == '\\') + { + // Use Start of Text (0x02) and End of Text (0x03) special + // characters as pre-formatted fragment markers. + // + pre = !pre; + t2 += (pre ? 0x02 : 0x03); + } + else if (b <= e) t2.append (t1, b, e - b + 1); if (more) @@ -1001,27 +1025,75 @@ doc_string (const char* l, size_t n) b = p + 1; } } + + if (pre) + { + cerr << *path_ << ": error: missing pre-formatted fragment end marker " + << "in documentation string '" << t1 << "'" << endl; + throw error (); + } } - // Replace every single newlines with single space and all - // multiple new lines (paragraph marker) with a single newline. + // Replace every single newlines with single space and all multiple new + // lines (paragraph marker) with a single newline, unless we are in a + // pre-formatted fragment. // - t1.clear (); + bool pre (false); for (size_t i (0), n (t2.size ()); i < n; ++i) { - if (t2[i] == '\n') + char c (t2[i]); + + if (c == '\n' && !pre) { size_t j (i); for (; i + 1 < n && t2[i + 1] == '\n'; ++i) ; if (j != 0 && i + 1 != n) // Strip leading and trailing newlines. - t1 += i != j ? '\n' : ' '; + t3 += i != j ? '\n' : ' '; } else - t1 += t2[i]; + { + if (c == (pre ? 0x03 : 0x02)) + { + pre = !pre; + + // Kill "inner" newlines (after opening and before closing '/' + // markers). Also check for "outer" newlines so that we always + // have paragraph separation. + // + size_t k (t3.size ()); + if (pre) + { + if (k != 0 && t3[k - 1] != '\n') // Outer. + { + cerr << *path_ << ": error: missing empty line before pre-" + << "formatted fragment start marker in documentation " + << "string '" << t1 << "'" << endl; + throw error (); + } + + ++i; // Skip inner. + } + else + { + if (t3[k - 1] == '\n') // Could be the same as opnening if empty. + t3.resize (k - 1); // Pop inner. + + if (i + 2 < n && (t2[i + 1] != '\n' || t2[i + 2] != '\n')) // Outer. + { + cerr << *path_ << ": error: missing empty line after pre-" + << "formatted fragment end marker in documentation " + << "string '" << t1 << "'" << endl; + throw error (); + } + } + } + + t3 += c; + } } - return t1; + return t3; } -- cgit v1.1