diff options
Diffstat (limited to 'mod/page.cxx')
-rw-r--r-- | mod/page.cxx | 304 |
1 files changed, 194 insertions, 110 deletions
diff --git a/mod/page.cxx b/mod/page.cxx index 1e317f0..bc2e42d 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -7,10 +7,10 @@ #include <cmark-gfm-extension_api.h> #include <set> -#include <ios> // hex, uppercase, right +#include <ios> // hex, uppercase, right #include <sstream> -#include <iomanip> // setw(), setfill() -#include <algorithm> // min(), find() +#include <iomanip> // setw(), setfill() +#include <iterator> // back_inserter() #include <libstudxml/serializer.hxx> @@ -36,6 +36,20 @@ using namespace web::xhtml; // namespace brep { + static inline string + label_to_class (const string& label) + { + if (label.find (' ') == string::npos) + return label; + + string r; + transform (label.begin (), label.end (), + back_inserter (r), + [] (char c) {return c != ' ' ? c : '-';}); + + return r; + } + // CSS_LINKS // static const dir_path css_path ("@"); @@ -123,9 +137,17 @@ namespace brep void DIV_COUNTER:: operator() (serializer& s) const { - s << DIV(ID="count") - << count_ << " " - << (count_ % 10 == 1 && count_ % 100 != 11 ? singular_ : plural_) + s << DIV(ID="count"); + + if (count_) + s << *count_; + else + s << '?'; + + s << ' ' + << (count_ && *count_ % 10 == 1 && *count_ % 100 != 11 + ? singular_ + : plural_) << ~DIV; } @@ -134,7 +156,8 @@ namespace brep void TR_VALUE:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") << value_ << ~SPAN << ~TD << ~TR; @@ -145,7 +168,8 @@ namespace brep void TR_INPUT:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << INPUT(TYPE="text", NAME=name_); @@ -169,7 +193,8 @@ namespace brep void TR_SELECT:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SELECT(NAME=name_); @@ -220,15 +245,9 @@ namespace brep << A << HREF << tenant_dir (root_, tenant_) / - path (mime_url_encode (name_.string (), false)); - - // Propagate search criteria to the package details page. - // - if (!query_.empty ()) - s << "?q=" << query_; - - s << ~HREF - << name_ + path (mime_url_encode (name_.string (), false)) + << ~HREF + << name_ << ~A << ~SPAN << ~TD @@ -416,47 +435,75 @@ namespace brep if (!dependencies_.empty ()) s << "; "; - for (const auto& d: dependencies_) + for (const dependency_alternatives& das: dependencies_) { - if (&d != &dependencies_[0]) + if (&das != &dependencies_[0]) s << ", "; - if (d.conditional) - s << "?"; - - if (d.buildtime) + if (das.buildtime) s << "*"; - // Suppress package name duplicates. + // Suppress dependency alternative duplicates, like in + // `{foo bar} < 1.1 | {foo bar} > 1.5`. + // + // Return the dependency package name space-separated list. // - set<package_name> names; - for (const auto& da: d) - names.emplace (da.name); + auto deps_list = [] (const dependency_alternative& da) + { + string r; + for (const dependency& d: da) + { + if (!r.empty ()) + r += ' '; + + r += d.name.string (); + } - bool mult (names.size () > 1); + return r; + }; + + set<string> alternatives; + for (const dependency_alternative& da: das) + alternatives.insert (deps_list (da)); + + // Note that we may end up with a single package name in parenthesis, if + // its duplicates were suppresses. This, however, may be helpful, + // indicating that there some alternatives for the package. + // + bool mult (das.size () > 1 || + (das.size () == 1 && das[0].size () > 1)); if (mult) - s << "("; + s << '('; bool first (true); - for (const auto& da: d) + for (const dependency_alternative& da: das) { - const package_name& n (da.name); - if (names.find (n) != names.end ()) - { - names.erase (n); + auto i (alternatives.find (deps_list (da))); - if (first) - first = false; - else - s << " | "; + if (i == alternatives.end ()) + continue; + + alternatives.erase (i); + + if (!first) + s << " | "; + else + first = false; + + for (const dependency& d: da) + { + if (&d != &da[0]) + s << ' '; // Try to display the dependency as a link if it is resolved. // Otherwise display it as plain text. // - if (da.package != nullptr) + const package_name& n (d.name); + + if (d.package != nullptr) { - shared_ptr<package> p (da.package.load ()); + shared_ptr<package> p (d.package.load ()); assert (p->internal () || !p->other_repositories.empty ()); shared_ptr<repository> r ( @@ -479,10 +526,13 @@ namespace brep else s << n; } + + if (da.enable) + s << " ?"; } if (mult) - s << ")"; + s << ')'; } s << ~SPAN @@ -507,25 +557,25 @@ namespace brep << SPAN(CLASS="value") << requirements_.size () << "; "; - for (const auto& r: requirements_) + for (const auto& ras: requirements_) { - if (&r != &requirements_[0]) + if (&ras != &requirements_[0]) s << ", "; - if (r.conditional) - s << "?"; + if (ras.buildtime) + s << '*'; - if (r.buildtime) - s << "*"; - - if (r.empty ()) + // If this is a simple requirement without id, then print the comment + // first word. + // + if (ras.simple () && ras[0][0].empty ()) { - // If there is no requirement alternatives specified, then print the - // comment first word. - // - const auto& c (r.comment); + const auto& c (ras.comment); if (!c.empty ()) { + if (ras[0].enable) + s << "? "; + auto n (c.find (' ')); s << string (c, 0, n); @@ -535,21 +585,31 @@ namespace brep } else { - bool mult (r.size () > 1); + bool mult (ras.size () > 1 || + (ras.size () == 1 && ras[0].size () > 1)); if (mult) - s << "("; + s << '('; - for (const auto& ra: r) + for (const auto& ra: ras) { - if (&ra != &r[0]) + if (&ra != &ras[0]) s << " | "; - s << ra; + for (const string& r: ra) + { + if (&r != &ra[0]) + s << ' '; + + s << r; + } + + if (ra.enable) + s << " ?"; } if (mult) - s << ")"; + s << ')'; } } @@ -563,7 +623,8 @@ namespace brep void TR_URL:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value"); @@ -593,7 +654,8 @@ namespace brep void TR_EMAIL:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") @@ -643,32 +705,22 @@ namespace brep << A << HREF << tenant_dir (root_, tenant_) << "?about#" - << mime_url_encode (html_id (name_), false) + << mime_url_encode (html_id (location_.canonical_name ()), false) << ~HREF - << name_ + << location_ << ~A << ~SPAN << ~TD << ~TR; } - // TR_LOCATION - // - void TR_LOCATION:: - operator() (serializer& s) const - { - s << TR(CLASS="location") - << TH << "location" << ~TH - << TD << SPAN(CLASS="value") << location_ << ~SPAN << ~TD - << ~TR; - } - // TR_LINK // void TR_LINK:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") << A(HREF=url_) << text_ << ~A << ~SPAN @@ -697,8 +749,24 @@ namespace brep << TD << SPAN(CLASS="value"); + // Print the ' | ' separator if this is not the first item and reset the + // `first` flag to false otherwise. + // + bool first (true); + auto separate = [&s, &first] () + { + if (first) + first = false; + else + s << " | "; + }; + if (build_.state == build_state::building) - s << SPAN(CLASS="building") << "building" << ~SPAN << " | "; + { + separate (); + + s << SPAN(CLASS="building") << "building" << ~SPAN; + } else { // If no unsuccessful operation results available, then print the @@ -711,7 +779,10 @@ namespace brep if (build_.results.empty () || *build_.status == result_status::success) { assert (build_.status); - s << SPAN_BUILD_RESULT_STATUS (*build_.status) << " | "; + + separate (); + + s << SPAN_BUILD_RESULT_STATUS (*build_.status); } if (!build_.results.empty ()) @@ -719,6 +790,9 @@ namespace brep for (const auto& r: build_.results) { if (r.status != result_status::success) + { + separate (); + s << SPAN_BUILD_RESULT_STATUS (r.status) << " (" << A << HREF @@ -726,26 +800,33 @@ namespace brep << ~HREF << r.operation << ~A - << ") | "; + << ")"; + } } + separate (); + s << A << HREF << build_log_url (host_, root_, build_) << ~HREF << "log" - << ~A - << " | "; + << ~A; } } - if (build_.force == (build_.state == build_state::building - ? force_state::forcing - : force_state::forced)) - s << SPAN(CLASS="pending") << "pending" << ~SPAN; - else - s << A - << HREF << build_force_url (host_, root_, build_) << ~HREF - << "rebuild" - << ~A; + if (!archived_) + { + separate (); + + if (build_.force == (build_.state == build_state::building + ? force_state::forcing + : force_state::forced)) + s << SPAN(CLASS="pending") << "pending" << ~SPAN; + else + s << A + << HREF << build_force_url (host_, root_, build_) << ~HREF + << "rebuild" + << ~A; + } s << ~SPAN << ~TD @@ -873,14 +954,16 @@ namespace brep void DIV_TEXT:: operator() (serializer& s) const { - switch (type_) + const string& t (text_.text); + + switch (text_.type) { case text_type::plain: { // To keep things regular we wrap the preformatted text into <div>. // s << DIV(ID=id_, CLASS="plain"); - serialize_pre_text (s, text_, length_, url_, "" /* id */); + serialize_pre_text (s, t, length_, url_, "" /* id */); s << ~DIV; break; } @@ -900,9 +983,9 @@ namespace brep // calls to fail is the inability to allocate memory. Unfortunately, // instead of reporting the failure to the caller, the API issues // diagnostics to stderr and aborts the process. Let's decrease the - // probability of such an event by limiting the text size to 64K. + // probability of such an event by limiting the text size to 1M. // - if (text_.size () > 64 * 1024) + if (t.size () > 1024 * 1024) { print_error (what_ + " is too long"); return; @@ -914,37 +997,38 @@ namespace brep { // Parse Markdown into the AST. // + // Note that the footnotes extension needs to be enabled via the + // CMARK_OPT_FOOTNOTES flag rather than the + // cmark_parser_attach_syntax_extension() function call. + // unique_ptr<cmark_parser, void (*)(cmark_parser*)> parser ( - cmark_parser_new (CMARK_OPT_DEFAULT | CMARK_OPT_VALIDATE_UTF8), + cmark_parser_new (CMARK_OPT_DEFAULT | + CMARK_OPT_FOOTNOTES | + CMARK_OPT_VALIDATE_UTF8), [] (cmark_parser* p) {cmark_parser_free (p);}); // Enable GitHub extensions in the parser, if requested. // - if (type_ == text_type::github_mark) + if (text_.type == text_type::github_mark) { auto add = [&parser] (const char* ext) - { - cmark_syntax_extension* e ( - cmark_find_syntax_extension (ext)); + { + cmark_syntax_extension* e ( + cmark_find_syntax_extension (ext)); - // Built-in extension is only expected. - // - assert (e != nullptr); + // Built-in extension is only expected. + // + assert (e != nullptr); - cmark_parser_attach_syntax_extension (parser.get (), e); - }; + cmark_parser_attach_syntax_extension (parser.get (), e); + }; add ("table"); add ("strikethrough"); add ("autolink"); - - // Somehow feels unsafe (there are some nasty warnings when - // upstream's tasklist.c is compiled), so let's disable for now. - // - // add ("tasklist"); } - cmark_parser_feed (parser.get (), text_.c_str (), text_.size ()); + cmark_parser_feed (parser.get (), t.c_str (), t.size ()); unique_ptr<cmark_node, void (*)(cmark_node*)> doc ( cmark_parser_finish (parser.get ()), |