this repo has no description
at develop 44 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Guido Tack <guido.tack@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#include <minizinc/config.hh> 13#include <minizinc/copy.hh> 14#include <minizinc/eval_par.hh> 15#include <minizinc/htmlprinter.hh> 16#include <minizinc/model.hh> 17#include <minizinc/prettyprinter.hh> 18 19#include <cctype> 20#include <sstream> 21 22namespace MiniZinc { 23 24namespace HtmlDocOutput { 25 26// Trim leading space: 27// - always trim first line completely 28// - second line defines the base indentation 29std::string trim(const std::string& s0) { 30 std::string s = s0; 31 // remove carriage returns 32 size_t j = 0; 33 for (size_t i = 0; i < s.size(); i++) { 34 if (s[i] != '\r') s[j++] = s[i]; 35 } 36 s.resize(j); 37 size_t first_line_indent = s.find_first_not_of(" \t"); 38 if (first_line_indent == std::string::npos) return ""; 39 size_t first_nl = s.find("\n"); 40 std::ostringstream oss; 41 if (first_line_indent == first_nl) { 42 // first line is empty 43 oss << "\n"; 44 std::cerr << "--empty first line\n"; 45 } else { 46 // strip first line 47 size_t end_of_first_line = 48 first_nl == std::string::npos ? std::string::npos : first_nl - first_line_indent + 1; 49 oss << s.substr(first_line_indent, end_of_first_line); 50 std::cerr << "--" << first_line_indent << ", " << first_nl << "\n"; 51 std::cerr << "'" << s.substr(first_line_indent, end_of_first_line) << "'\n"; 52 } 53 if (first_nl == std::string::npos) return oss.str(); 54 size_t unindent = s.find_first_not_of(" \t", first_nl + 1); 55 if (unindent == std::string::npos) return oss.str(); 56 size_t pos = s.find("\n", first_nl + 1); 57 if (unindent == 0 || unindent > pos) { 58 oss << s.substr(first_nl + 1, std::string::npos); 59 return oss.str(); 60 } 61 size_t lastpos = unindent; 62 while (pos != std::string::npos) { 63 oss << s.substr(lastpos, pos - lastpos) << "\n"; 64 size_t next_indent = s.find_first_not_of(" \t", pos + 1); 65 if (next_indent == std::string::npos) { 66 lastpos = next_indent; 67 } else if (next_indent - (pos + 1) < unindent) { 68 lastpos = next_indent; 69 } else { 70 lastpos = pos + 1 + unindent; 71 } 72 pos = (lastpos == std::string::npos ? lastpos : s.find("\n", lastpos)); 73 } 74 if (lastpos != std::string::npos) oss << s.substr(lastpos, std::string::npos); 75 return oss.str(); 76} 77 78class DocItem { 79public: 80 enum DocType { T_PAR = 0, T_VAR = 1, T_FUN = 2 }; 81 DocItem(const DocType& t0, std::string id0, std::string sig0, std::string doc0) 82 : t(t0), id(id0), sig(sig0), doc(doc0) {} 83 DocType t; 84 std::string id; 85 std::string sig; 86 std::string doc; 87}; 88 89typedef std::unordered_map<FunctionI*, std::string> FunMap; 90 91class Group; 92 93class GroupMap { 94public: 95 typedef std::vector<Group*> Map; 96 Map m; 97 ~GroupMap(); 98 Map::iterator find(const std::string& n); 99}; 100 101class Group { 102public: 103 Group(const std::string& name0, const std::string& fullPath0) 104 : name(name0), fullPath(fullPath0) {} 105 std::string name; 106 std::string fullPath; 107 std::string desc; 108 std::string htmlName; 109 GroupMap subgroups; 110 std::vector<DocItem> items; 111 112 std::string getAnchor(int level, int indivFileLevel) { 113 if (level < indivFileLevel) { 114 return fullPath + ".html"; 115 } else { 116 return "#" + fullPath; 117 } 118 } 119 120 std::string toHTML(int level, int indivFileLevel, Group* parent, int idx, 121 const std::string& basename, bool generateIndex) { 122 std::ostringstream oss; 123 124 int realLevel = (level < indivFileLevel) ? 0 : level - indivFileLevel; 125 oss << "<div class='mzn-group-level-" << realLevel << "'>\n"; 126 if (parent) { 127 oss << "<div class='mzn-group-nav'>"; 128 if (idx > 0) { 129 oss << "<a class='mzn-nav-prev' href='" 130 << parent->subgroups.m[idx - 1]->getAnchor(level - 1, indivFileLevel) << "' title='" 131 << parent->subgroups.m[idx - 1]->htmlName << "'>&#8656;</a> "; 132 } 133 oss << "<a class='mzn-nav-up' href='" << parent->getAnchor(level - 1, indivFileLevel) 134 << "' title='" << parent->htmlName << "'>&#8679;</a> "; 135 if (idx < parent->subgroups.m.size() - 1) { 136 oss << "<a class='mzn-nav-next' href='" 137 << parent->subgroups.m[idx + 1]->getAnchor(level - 1, indivFileLevel) << "' title='" 138 << parent->subgroups.m[idx + 1]->htmlName << "'>&#8658;</a> "; 139 } 140 if (generateIndex) oss << "<a href='doc-index.html'>Index</a>\n"; 141 if (items.size() > 0) { 142 oss << "<a href='javascript:void(0)' onclick='revealAll()' class='mzn-nav-text'>reveal " 143 "all</a>\n"; 144 oss << "<a href='javascript:void(0)' onclick='hideAll()' class='mzn-nav-text'>hide " 145 "all</a>\n"; 146 } 147 oss << "</div>"; 148 } 149 if (!htmlName.empty()) { 150 oss << "<div class='mzn-group-name'><a name='" << fullPath << "'>" << htmlName 151 << "</a></div>\n"; 152 oss << "<div class='mzn-group-desc'>\n" << desc << "</div>\n"; 153 } 154 155 if (subgroups.m.size() != 0) { 156 oss << "<p>Sections:</p>\n"; 157 oss << "<ul>\n"; 158 for (GroupMap::Map::iterator it = subgroups.m.begin(); it != subgroups.m.end(); ++it) { 159 oss << "<li><a href='" << (*it)->getAnchor(level, indivFileLevel) << "'>" << (*it)->htmlName 160 << "</a>\n"; 161 162 if ((*it)->htmlName.empty()) { 163 std::cerr << "Warning: undocumented group " << (*it)->fullPath << "\n"; 164 } 165 } 166 oss << "</ul>\n"; 167 if (parent == NULL && generateIndex) { 168 oss << "<p><a href='doc-index.html'>Index</a></p>\n"; 169 } 170 if (items.size() > 0) oss << "<p>Declarations in this section:</p>\n"; 171 } 172 173 struct SortById { 174 bool operator()(const DocItem& i0, const DocItem& i1) { 175 return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); 176 } 177 } _cmp; 178 std::stable_sort(items.begin(), items.end(), _cmp); 179 180 int cur_t = -1; 181 const char* dt[] = {"par", "var", "fun"}; 182 const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; 183 for (std::vector<DocItem>::const_iterator it = items.begin(); it != items.end(); ++it) { 184 if (it->t != cur_t) { 185 if (cur_t != -1) oss << "</div>\n"; 186 cur_t = it->t; 187 oss << "<div class='mzn-decl-type-" << dt[cur_t] << "'>\n"; 188 oss << "<div class='mzn-decl-type-heading'>" << dt_desc[cur_t] << "</div>\n"; 189 } 190 oss << it->doc; 191 } 192 if (cur_t != -1) oss << "</div>\n"; 193 194 if (level >= indivFileLevel) { 195 for (unsigned int i = 0; i < subgroups.m.size(); i++) { 196 oss << subgroups.m[i]->toHTML(level + 1, indivFileLevel, this, i, basename, generateIndex); 197 } 198 } 199 200 oss << "</div>"; 201 return oss.str(); 202 } 203 204 static std::string rstHeading(std::string s, int level) { 205 std::vector<char> levelChar({'#', '=', '-', '^', '+', '"'}); 206 std::ostringstream oss; 207 oss << s << "\n"; 208 for (int i = 0; i < s.size(); i++) oss << levelChar[level]; 209 oss << "\n\n"; 210 return oss.str(); 211 } 212 213 std::string toRST(int level) { 214 std::ostringstream oss; 215 if (!htmlName.empty()) { 216 if (level == 0) { 217 oss << ".. _ch-lib-" << name << ":\n\n"; 218 } 219 oss << rstHeading(htmlName, level); 220 oss << HtmlDocOutput::trim(desc) << "\n\n"; 221 } 222 for (unsigned int i = 0; i < subgroups.m.size(); i++) { 223 oss << subgroups.m[i]->toRST(level + 1); 224 } 225 if (items.size() > 0) { 226 if (subgroups.m.size() != 0) { 227 oss << rstHeading("Other declarations", level + 1); 228 } 229 struct SortById { 230 bool operator()(const DocItem& i0, const DocItem& i1) { 231 return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); 232 } 233 } _cmp; 234 std::stable_sort(items.begin(), items.end(), _cmp); 235 236 int cur_t = -1; 237 int nHeadings = 0; 238 for (std::vector<DocItem>::const_iterator it = items.begin(); it != items.end(); ++it) { 239 if (it->t != cur_t) { 240 cur_t = it->t; 241 nHeadings++; 242 } 243 } 244 cur_t = -1; 245 const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; 246 for (std::vector<DocItem>::const_iterator it = items.begin(); it != items.end(); ++it) { 247 if (it->t != cur_t) { 248 cur_t = it->t; 249 if (nHeadings > 1) 250 oss << rstHeading(dt_desc[cur_t], subgroups.m.size() == 0 ? level + 1 : level + 2); 251 } 252 oss << it->doc; 253 } 254 } 255 return oss.str(); 256 } 257}; 258 259GroupMap::~GroupMap() { 260 for (Map::iterator it = m.begin(); it != m.end(); ++it) { 261 delete *it; 262 } 263} 264GroupMap::Map::iterator GroupMap::find(const std::string& n) { 265 for (Map::iterator it = m.begin(); it != m.end(); ++it) 266 if ((*it)->name == n) return it; 267 return m.end(); 268} 269 270void addToGroup(Group& gm, const std::string& group, DocItem& di) { 271 std::vector<std::string> subgroups; 272 size_t lastpos = 0; 273 size_t pos = group.find("."); 274 while (pos != std::string::npos) { 275 subgroups.push_back(group.substr(lastpos, pos - lastpos)); 276 lastpos = pos + 1; 277 pos = group.find(".", lastpos); 278 } 279 subgroups.push_back(group.substr(lastpos, std::string::npos)); 280 281 GroupMap* cgm = &gm.subgroups; 282 std::string gpath(gm.fullPath); 283 for (unsigned int i = 0; i < subgroups.size(); i++) { 284 gpath += "-"; 285 gpath += subgroups[i]; 286 if (cgm->find(subgroups[i]) == cgm->m.end()) { 287 cgm->m.push_back(new Group(subgroups[i], gpath)); 288 } 289 Group& g = **cgm->find(subgroups[i]); 290 if (i == subgroups.size() - 1) { 291 g.items.push_back(di); 292 } else { 293 cgm = &g.subgroups; 294 } 295 } 296} 297 298void setGroupDesc(Group& maingroup, const std::string& group, std::string htmlName, std::string s) { 299 if (group == "MAIN") { 300 if (!maingroup.htmlName.empty()) { 301 std::cerr << "Warning: two descriptions for group `" << group << "'\n"; 302 } 303 maingroup.htmlName = htmlName; 304 maingroup.desc = s; 305 return; 306 } 307 308 std::vector<std::string> subgroups; 309 size_t lastpos = 0; 310 size_t pos = group.find("."); 311 while (pos != std::string::npos) { 312 subgroups.push_back(group.substr(lastpos, pos - lastpos)); 313 lastpos = pos + 1; 314 pos = group.find(".", lastpos); 315 } 316 subgroups.push_back(group.substr(lastpos, std::string::npos)); 317 318 GroupMap* cgm = &maingroup.subgroups; 319 std::string gpath(maingroup.fullPath); 320 for (unsigned int i = 0; i < subgroups.size(); i++) { 321 gpath += "-"; 322 gpath += subgroups[i]; 323 if (cgm->find(subgroups[i]) == cgm->m.end()) { 324 cgm->m.push_back(new Group(subgroups[i], gpath)); 325 } 326 Group& g = **cgm->find(subgroups[i]); 327 if (i == subgroups.size() - 1) { 328 if (!g.htmlName.empty()) { 329 std::cerr << "Warning: two descriptions for group `" << group << "'\n"; 330 } 331 g.htmlName = htmlName; 332 g.desc = s; 333 } else { 334 cgm = &g.subgroups; 335 } 336 } 337} 338 339std::string extractArgWord(std::string& s, size_t n) { 340 size_t start = n; 341 while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; 342 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; 343 size_t end = start + 1; 344 while (end < s.size() && (isalnum(s[end]) || s[end] == '_' || s[end] == '.')) end++; 345 std::string ret = s.substr(start, end - start); 346 s = s.substr(end, std::string::npos); 347 return ret; 348} 349 350std::string makeHTMLId(const std::string& ident) { 351 std::ostringstream oss; 352 oss << "I"; 353 bool prevWasSym = false; 354 for (size_t i = 0; i < ident.size(); i++) { 355 bool isSym = true; 356 switch (ident[i]) { 357 case '!': 358 oss << "-ex"; 359 break; 360 case '=': 361 oss << "-eq"; 362 break; 363 case '*': 364 oss << "-as"; 365 break; 366 case '+': 367 oss << "-pl"; 368 break; 369 case '-': 370 oss << "-mi"; 371 break; 372 case '>': 373 oss << "-gr"; 374 break; 375 case '<': 376 oss << "-lt"; 377 break; 378 case '/': 379 oss << "-dv"; 380 break; 381 case '\\': 382 oss << "-bs"; 383 break; 384 case '~': 385 oss << "-tl"; 386 break; 387 case '\'': 388 oss << "-tk"; 389 break; 390 case ' ': 391 break; 392 case '\t': 393 break; 394 case '\n': 395 break; 396 case ':': 397 oss << "-cl"; 398 break; 399 case '[': 400 oss << "-bo"; 401 break; 402 case ']': 403 oss << "-bc"; 404 break; 405 case '$': 406 oss << "-dd"; 407 break; 408 case '(': 409 oss << "-po"; 410 break; 411 case ')': 412 oss << "-pc"; 413 break; 414 case ',': 415 oss << "-cm"; 416 break; 417 default: 418 oss << (prevWasSym ? "-" : "") << ident[i]; 419 isSym = false; 420 break; 421 } 422 prevWasSym = isSym; 423 } 424 return oss.str(); 425} 426 427} // namespace HtmlDocOutput 428 429class CollectFunctionsVisitor : public ItemVisitor { 430protected: 431 EnvI& env; 432 HtmlDocOutput::FunMap& _funmap; 433 bool _includeStdLib; 434 435public: 436 CollectFunctionsVisitor(EnvI& env0, HtmlDocOutput::FunMap& funmap, bool includeStdLib) 437 : env(env0), _funmap(funmap), _includeStdLib(includeStdLib) {} 438 bool enterModel(Model* m) { return _includeStdLib || m->filename() != "stdlib.mzn"; } 439 void vFunctionI(FunctionI* fi) { 440 if (Call* docstring = 441 Expression::dyn_cast<Call>(getAnnotation(fi->ann(), constants().ann.doc_comment))) { 442 std::string ds = eval_string(env, docstring->arg(0)); 443 std::string group("main"); 444 size_t group_idx = ds.find("@group"); 445 if (group_idx != std::string::npos) { 446 group = HtmlDocOutput::extractArgWord(ds, group_idx); 447 } 448 _funmap.insert(std::make_pair(fi, group)); 449 } 450 } 451}; 452 453class PrintHtmlVisitor : public ItemVisitor { 454protected: 455 EnvI& env; 456 HtmlDocOutput::Group& _maingroup; 457 HtmlDocOutput::FunMap& _funmap; 458 bool _includeStdLib; 459 460 std::vector<std::string> replaceArgs(std::string& s) { 461 std::vector<std::string> replacements; 462 std::ostringstream oss; 463 size_t lastpos = 0; 464 size_t pos = std::min(s.find("\\a"), s.find("\\p")); 465 size_t mathjax_open = s.find("\\("); 466 size_t mathjax_close = s.rfind("\\)"); 467 if (pos == std::string::npos) return replacements; 468 while (pos != std::string::npos) { 469 oss << s.substr(lastpos, pos - lastpos); 470 size_t start = pos; 471 while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; 472 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; 473 size_t end = start + 1; 474 while (end < s.size() && (isalnum(s[end]) || s[end] == '_')) end++; 475 if (s[pos + 1] == 'a') { 476 replacements.push_back(s.substr(start, end - start)); 477 if (pos >= mathjax_open && pos <= mathjax_close) { 478 oss << "{\\bf " << replacements.back() << "}"; 479 } else { 480 oss << "<span class='mzn-arg'>" << replacements.back() << "</span>"; 481 } 482 } else { 483 if (pos >= mathjax_open && pos <= mathjax_close) { 484 oss << "{\\bf " << s.substr(start, end - start) << "}"; 485 } else { 486 oss << "<span class='mzn-parm'>" << s.substr(start, end - start) << "</span>"; 487 } 488 } 489 lastpos = end; 490 pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); 491 } 492 oss << s.substr(lastpos, std::string::npos); 493 s = oss.str(); 494 return replacements; 495 } 496 497 std::pair<std::string, std::string> extractArgLine(std::string& s, size_t n) { 498 size_t start = n; 499 while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; 500 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; 501 size_t end = start + 1; 502 while (end < s.size() && s[end] != ':') end++; 503 std::string arg = s.substr(start, end - start); 504 size_t doc_start = end + 1; 505 while (end < s.size() && s[end] != '\n') end++; 506 std::string ret = s.substr(doc_start, end - doc_start); 507 replaceArgs(ret); 508 s = s.substr(0, n) + s.substr(end, std::string::npos); 509 return make_pair(arg, ret); 510 } 511 512 std::string addHTML(const std::string& s) { 513 std::ostringstream oss; 514 size_t lastpos = 0; 515 size_t pos = s.find('\n'); 516 bool inUl = false; 517 oss << "<p>\n"; 518 while (pos != std::string::npos) { 519 oss << s.substr(lastpos, pos - lastpos); 520 size_t next = std::min(s.find('\n', pos + 1), s.find('-', pos + 1)); 521 if (next == std::string::npos) { 522 lastpos = pos + 1; 523 break; 524 } 525 bool allwhite = true; 526 for (size_t cur = pos + 1; cur < next; cur++) { 527 if (s[cur] != ' ' && s[cur] != '\t') { 528 allwhite = false; 529 break; 530 } 531 } 532 if (allwhite) { 533 if (s[next] == '-') { 534 if (!inUl) { 535 oss << "<ul>\n"; 536 inUl = true; 537 } 538 oss << "<li>"; 539 } else { 540 if (inUl) { 541 oss << "</ul>\n"; 542 inUl = false; 543 } else { 544 oss << "</p><p>\n"; 545 } 546 } 547 lastpos = next + 1; 548 pos = s.find('\n', lastpos); 549 } else { 550 lastpos = pos + 1; 551 if (s[pos] == '\n') { 552 oss << " "; 553 } 554 if (s[next] == '-') { 555 pos = s.find('\n', next + 1); 556 } else { 557 pos = next; 558 } 559 } 560 } 561 oss << s.substr(lastpos, std::string::npos); 562 if (inUl) oss << "</ul>\n"; 563 oss << "</p>\n"; 564 return oss.str(); 565 } 566 567public: 568 PrintHtmlVisitor(EnvI& env0, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, 569 bool includeStdLib) 570 : env(env0), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} 571 bool enterModel(Model* m) { 572 if (!_includeStdLib && m->filename() == "stdlib.mzn") return false; 573 const std::string& dc = m->docComment(); 574 if (!dc.empty()) { 575 size_t gpos = dc.find("@groupdef"); 576 while (gpos != std::string::npos) { 577 size_t start = gpos; 578 while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') start++; 579 while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) start++; 580 size_t end = start + 1; 581 while (end < dc.size() && (isalnum(dc[end]) || dc[end] == '_' || dc[end] == '.')) end++; 582 std::string groupName = dc.substr(start, end - start); 583 size_t doc_start = end + 1; 584 while (end < dc.size() && dc[end] != '\n') end++; 585 std::string groupHTMLName = dc.substr(doc_start, end - doc_start); 586 587 size_t next = dc.find("@groupdef", gpos + 1); 588 HtmlDocOutput::setGroupDesc( 589 _maingroup, groupName, groupHTMLName, 590 addHTML(dc.substr(end, next == std::string::npos ? next : next - end))); 591 gpos = next; 592 } 593 } 594 return true; 595 } 596 /// Visit variable declaration 597 void vVarDeclI(VarDeclI* vdi) { 598 if (Call* docstring = Expression::dyn_cast<Call>( 599 getAnnotation(vdi->e()->ann(), constants().ann.doc_comment))) { 600 std::string ds = eval_string(env, docstring->arg(0)); 601 std::string group("main"); 602 size_t group_idx = ds.find("@group"); 603 if (group_idx != std::string::npos) { 604 group = HtmlDocOutput::extractArgWord(ds, group_idx); 605 } 606 607 std::ostringstream os; 608 std::string sig = vdi->e()->type().toString(env) + " " + vdi->e()->id()->str().str(); 609 os << "<div class='mzn-vardecl' id='" << HtmlDocOutput::makeHTMLId(sig) << "'>\n"; 610 os << "<div class='mzn-vardecl-code'>\n"; 611 if (vdi->e()->ti()->type() == Type::ann()) { 612 os << "<span class='mzn-kw'>annotation</span> "; 613 os << "<span class='mzn-fn-id'>" << *vdi->e()->id() << "</span>"; 614 } else { 615 os << *vdi->e()->ti() << ": " << *vdi->e()->id(); 616 } 617 os << "</div><div class='mzn-vardecl-doc'>\n"; 618 os << addHTML(ds); 619 os << "</div></div>"; 620 GCLock lock; 621 HtmlDocOutput::DocItem di( 622 vdi->e()->type().ispar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, 623 sig, sig, os.str()); 624 HtmlDocOutput::addToGroup(_maingroup, group, di); 625 } 626 } 627 /// Visit function item 628 void vFunctionI(FunctionI* fi) { 629 if (Call* docstring = 630 Expression::dyn_cast<Call>(getAnnotation(fi->ann(), constants().ann.doc_comment))) { 631 std::string ds = eval_string(env, docstring->arg(0)); 632 std::string group("main"); 633 size_t group_idx = ds.find("@group"); 634 if (group_idx != std::string::npos) { 635 group = HtmlDocOutput::extractArgWord(ds, group_idx); 636 } 637 638 size_t param_idx = ds.find("@param"); 639 std::vector<std::pair<std::string, std::string> > params; 640 while (param_idx != std::string::npos) { 641 params.push_back(extractArgLine(ds, param_idx)); 642 param_idx = ds.find("@param"); 643 } 644 645 std::vector<std::string> args = replaceArgs(ds); 646 647 std::unordered_set<std::string> allArgs; 648 for (unsigned int i = 0; i < args.size(); i++) allArgs.insert(args[i]); 649 for (unsigned int i = 0; i < params.size(); i++) allArgs.insert(params[i].first); 650 651 GCLock lock; 652 for (unsigned int i = 0; i < fi->params().size(); i++) { 653 if (allArgs.find(fi->params()[i]->id()->str().str()) == allArgs.end()) { 654 std::cerr << "Warning: parameter " << *fi->params()[i]->id() 655 << " not documented for function " << fi->id() << " at location " << fi->loc() 656 << "\n"; 657 } 658 } 659 660 std::string sig; 661 { 662 GCLock lock; 663 FunctionI* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); 664 std::ostringstream oss_sig; 665 oss_sig << *fi_c; 666 sig = oss_sig.str(); 667 sig.resize(sig.size() - 2); 668 } 669 670 std::ostringstream os; 671 os << "<div class='mzn-fundecl' id='" << HtmlDocOutput::makeHTMLId(sig) << "'>\n"; 672 os << "<div class='mzn-fundecl-code'>"; 673 os << "<a href='javascript:void(0)' onclick='revealMore(this)' " 674 "class='mzn-fundecl-more'>&#9664;</a>"; 675 676 std::ostringstream fs; 677 if (fi->ti()->type() == Type::ann()) { 678 fs << "annotation "; 679 os << "<span class='mzn-kw'>annotation</span> "; 680 } else if (fi->ti()->type() == Type::parbool()) { 681 fs << "test "; 682 os << "<span class='mzn-kw'>test</span> "; 683 } else if (fi->ti()->type() == Type::varbool()) { 684 fs << "predicate "; 685 os << "<span class='mzn-kw'>predicate</span> "; 686 } else { 687 fs << "function " << *fi->ti() << ": "; 688 os << "<span class='mzn-kw'>function</span> <span class='mzn-ti'>" << *fi->ti() 689 << "</span>: "; 690 } 691 fs << fi->id() << "("; 692 os << "<span class='mzn-fn-id'>" << fi->id() << "</span>("; 693 size_t align = fs.str().size(); 694 for (unsigned int i = 0; i < fi->params().size(); i++) { 695 fs << *fi->params()[i]->ti() << ": " << *fi->params()[i]->id(); 696 if (i < fi->params().size() - 1) { 697 fs << ", "; 698 } 699 } 700 bool splitArgs = (fs.str().size() > 70); 701 for (unsigned int i = 0; i < fi->params().size(); i++) { 702 os << "<span class='mzn-ti'>" << *fi->params()[i]->ti() << "</span>: " 703 << "<span class='mzn-id'>" << *fi->params()[i]->id() << "</span>"; 704 if (i < fi->params().size() - 1) { 705 os << ","; 706 if (splitArgs) { 707 os << "\n"; 708 for (unsigned int j = static_cast<unsigned int>(align); j--;) os << " "; 709 } else { 710 os << " "; 711 } 712 } 713 } 714 os << ")"; 715 716 if (fi->e()) { 717 FunctionI* f_body = fi; 718 bool alias; 719 do { 720 alias = false; 721 Call* c = Expression::dyn_cast<Call>(f_body->e()); 722 if (c && c->n_args() == f_body->params().size()) { 723 bool sameParams = true; 724 for (unsigned int i = 0; i < f_body->params().size(); i++) { 725 Id* ident = c->arg(i)->dyn_cast<Id>(); 726 if (ident == NULL || ident->decl() != f_body->params()[i] || 727 ident->str() != c->decl()->params()[i]->id()->str()) { 728 sameParams = false; 729 break; 730 } 731 } 732 if (sameParams) { 733 alias = true; 734 f_body = c->decl(); 735 } 736 } 737 } while (alias); 738 if (f_body->e()) { 739 std::ostringstream body_os; 740 Printer p(body_os, 70); 741 p.print(f_body->e()); 742 743 std::string filename = f_body->loc().filename().str(); 744 size_t lastSlash = filename.find_last_of("/"); 745 if (lastSlash != std::string::npos) { 746 filename = filename.substr(lastSlash + 1, std::string::npos); 747 } 748 os << "<span class='mzn-fundecl-equals'> =</span>"; 749 os << "\n<div class='mzn-fundecl-more-code'>"; 750 os << "<div class='mzn-fundecl-body'>"; 751 os << body_os.str(); 752 os << "</div>\n"; 753 os << "(standard decomposition from " << filename << ":" << f_body->loc().first_line() 754 << ")"; 755 os << "</div>"; 756 } 757 } 758 759 os << "</div>\n<div class='mzn-fundecl-doc'>\n"; 760 761 if (fi->id().c_str()[0] == '\'') { 762 std::string op = fi->id().str(); 763 op = op.substr(1, op.length() - 2); 764 const char* space = (op[0] >= 'a' ? " " : ""); 765 if (fi->params().size() == 2) { 766 os << "<p>Usage: <span class=\"mzn-arg\">" << *fi->params()[0]->id() << space << op 767 << space << *fi->params()[1]->id() << "</span></p>"; 768 } else if (fi->params().size() == 1) { 769 os << "<p>Usage: <span class=\"mzn-arg\">" << op << space << *fi->params()[0]->id() 770 << "</span></p>"; 771 } 772 } 773 774 std::string dshtml = addHTML(ds); 775 776 os << dshtml; 777 if (params.size() > 0) { 778 os << "<div class='mzn-fundecl-params-heading'>Parameters</div>\n"; 779 os << "<ul class='mzn-fundecl-params'>\n"; 780 for (unsigned int i = 0; i < params.size(); i++) { 781 os << "<li><span class='mzn-arg'>" << params[i].first << "</span>: " << params[i].second 782 << "</li>\n"; 783 } 784 os << "</ul>\n"; 785 } 786 os << "</div>"; 787 os << "</div>"; 788 789 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, fi->id().str(), sig, os.str()); 790 HtmlDocOutput::addToGroup(_maingroup, group, di); 791 } 792 } 793}; 794 795std::vector<HtmlDocument> HtmlPrinter::printHtml(EnvI& env, MiniZinc::Model* m, 796 const std::string& basename, int splitLevel, 797 bool includeStdLib, bool generateIndex) { 798 using namespace HtmlDocOutput; 799 Group g(basename, basename); 800 FunMap funMap; 801 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 802 iterItems(fv, m); 803 PrintHtmlVisitor phv(env, g, funMap, includeStdLib); 804 iterItems(phv, m); 805 806 std::vector<HtmlDocument> ret; 807 808 struct SI { 809 Group* g; 810 Group* p; 811 int level; 812 int idx; 813 SI(Group* g0, Group* p0, int level0, int idx0) : g(g0), p(p0), level(level0), idx(idx0) {} 814 }; 815 816 struct IndexEntry { 817 std::string id; 818 std::string sig; 819 std::string link; 820 std::string groupName; 821 IndexEntry(const std::string& id0, const std::string& sig0, const std::string& link0, 822 const std::string& groupName0) 823 : id(id0), sig(sig0), link(link0), groupName(groupName0) { 824 size_t spacepos = id.find_last_of(' '); 825 if (spacepos != std::string::npos) { 826 id = id.substr(spacepos + 1); 827 } 828 } 829 bool operator<(const IndexEntry& e) const { 830 if (!isalpha(id[0]) && isalpha(e.id[0])) return true; 831 return id == e.id ? groupName < e.groupName : id < e.id; 832 } 833 }; 834 std::vector<IndexEntry> index; 835 836 std::vector<SI> stack; 837 stack.push_back(SI(&g, NULL, 0, 0)); 838 while (!stack.empty()) { 839 Group& g = *stack.back().g; 840 int curLevel = stack.back().level; 841 int curIdx = stack.back().idx; 842 Group* p = stack.back().p; 843 stack.pop_back(); 844 for (auto it : g.items) { 845 index.push_back(IndexEntry(it.id, it.sig, g.fullPath, g.htmlName)); 846 } 847 ret.push_back(HtmlDocument(g.fullPath, g.htmlName, 848 g.toHTML(curLevel, splitLevel, p, curIdx, basename, generateIndex))); 849 if (curLevel < splitLevel) { 850 for (unsigned int i = 0; i < g.subgroups.m.size(); i++) { 851 stack.push_back(SI(g.subgroups.m[i], &g, curLevel + 1, i)); 852 } 853 } 854 } 855 856 if (generateIndex) { 857 std::sort(index.begin(), index.end()); 858 std::ostringstream oss; 859 index.push_back(IndexEntry("", "", "", "")); 860 861 std::vector<std::string> idxSections; 862 863 if (index.size() != 0) { 864 if (isalpha(index[0].id[0])) { 865 char idxSec_c = (char)toupper(index[0].id[0]); 866 std::string idxSec(&idxSec_c, 1); 867 oss << "<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3>\n"; 868 idxSections.push_back(idxSec); 869 } else { 870 oss << "<h3 id='IdxSymbols'>Symbols</h3>\n"; 871 idxSections.push_back("Symbols"); 872 } 873 } 874 oss << "<ul>\n"; 875 std::string prevId = index.size() == 0 ? "" : index[0].id; 876 std::vector<IndexEntry> curEntries; 877 for (auto ie : index) { 878 if (ie.id != prevId) { 879 oss << "<li>"; 880 assert(curEntries.size() != 0); 881 IndexEntry& cur = curEntries[0]; 882 if (curEntries.size() == 1) { 883 oss << cur.id << " <a href='" << cur.link << ".html#" 884 << HtmlDocOutput::makeHTMLId(cur.sig) << "'>" 885 << "(" << cur.groupName << ")</a>"; 886 } else { 887 oss << cur.id << " ("; 888 bool first = true; 889 for (auto i_ie : curEntries) { 890 if (first) { 891 first = false; 892 } else { 893 oss << ", "; 894 } 895 oss << "<a href='" << i_ie.link << ".html#" << HtmlDocOutput::makeHTMLId(i_ie.sig) 896 << "'>"; 897 oss << i_ie.groupName << "</a>"; 898 } 899 oss << ")"; 900 } 901 oss << "</li>\n"; 902 curEntries.clear(); 903 } 904 if (isalpha(ie.id[0]) && ie.id[0] != prevId[0]) { 905 char idxSec_c = (char)toupper(ie.id[0]); 906 std::string idxSec(&idxSec_c, 1); 907 oss << "</ul>\n<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3><ul>"; 908 idxSections.push_back(idxSec); 909 } 910 prevId = ie.id; 911 if (curEntries.size() == 0 || curEntries.back().groupName != ie.groupName) { 912 curEntries.push_back(ie); 913 } 914 } 915 oss << "</ul>\n"; 916 917 std::ostringstream oss_header; 918 oss_header << "<div class='mzn-group-level-0'>\n"; 919 oss_header << "<div class='mzn-group-nav'>"; 920 oss_header << "<a class='mzn-nav-up' href='" << g.getAnchor(0, 1) << "' title='" << g.htmlName 921 << "'>&#8679;</a> "; 922 bool first = true; 923 for (auto is : idxSections) { 924 if (first) { 925 first = false; 926 } else { 927 oss_header << " | "; 928 } 929 oss_header << "<a href='#Idx" << is << "'>" << is << "</a>"; 930 } 931 932 oss_header << "</div>"; 933 934 oss_header << "<div class='mzn-group-name'>Index</div>\n"; 935 936 HtmlDocument idx("doc-index", "Index", oss_header.str() + oss.str()); 937 ret.push_back(idx); 938 } 939 return ret; 940} 941 942class PrintRSTVisitor : public ItemVisitor { 943protected: 944 EnvI& env; 945 HtmlDocOutput::Group& _maingroup; 946 HtmlDocOutput::FunMap& _funmap; 947 bool _includeStdLib; 948 949 std::vector<std::string> replaceArgsRST(std::string& s) { 950 std::vector<std::string> replacements; 951 std::ostringstream oss; 952 size_t lastpos = 0; 953 size_t pos = std::min(s.find("\\a"), s.find("\\p")); 954 size_t mathjax_open = s.find("\\("); 955 size_t mathjax_close = s.rfind("\\)"); 956 if (pos == std::string::npos) return replacements; 957 while (pos != std::string::npos) { 958 oss << s.substr(lastpos, pos - lastpos); 959 size_t start = pos; 960 while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; 961 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; 962 size_t end = start + 1; 963 while (end < s.size() && (isalnum(s[end]) || s[end] == '_')) end++; 964 bool needSpace = pos != 0 && s[pos - 1] != ' ' && s[pos - 1] != '\n'; 965 if (s[pos + 1] == 'a') { 966 replacements.push_back(s.substr(start, end - start)); 967 if (pos >= mathjax_open && pos <= mathjax_close) { 968 oss << "{\\bf " << replacements.back() << "}"; 969 } else { 970 oss << (needSpace ? " " : "") << "``" << replacements.back() << "`` "; 971 } 972 } else { 973 if (pos >= mathjax_open && pos <= mathjax_close) { 974 oss << "{\\bf " << s.substr(start, end - start) << "}"; 975 } else { 976 oss << (needSpace ? " " : "") << "``" << s.substr(start, end - start) << "`` "; 977 } 978 } 979 lastpos = end; 980 pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); 981 } 982 oss << s.substr(lastpos, std::string::npos); 983 s = oss.str(); 984 985 std::ostringstream oss2; 986 pos = std::min(s.find("\\("), s.find("\\)")); 987 lastpos = 0; 988 while (pos != std::string::npos) { 989 if (s[pos + 1] == ')') { 990 // remove trailing whitespace 991 std::string t = s.substr(lastpos, pos - lastpos); 992 size_t t_end = t.find_last_not_of(" "); 993 if (t_end != std::string::npos) t_end++; 994 oss2 << t.substr(0, t_end); 995 } else { 996 oss2 << s.substr(lastpos, pos - lastpos); 997 } 998 lastpos = pos + 2; 999 if (s[pos + 1] == '(') { 1000 oss2 << ":math:`"; 1001 lastpos = s.find_first_not_of(" ", lastpos); 1002 } else { 1003 oss2 << "`"; 1004 } 1005 pos = std::min(s.find("\\(", lastpos), s.find("\\)", lastpos)); 1006 } 1007 oss2 << s.substr(lastpos, std::string::npos); 1008 s = oss2.str(); 1009 return replacements; 1010 } 1011 1012 std::pair<std::string, std::string> extractArgLine(std::string& s, size_t n) { 1013 size_t start = n; 1014 while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; 1015 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; 1016 size_t end = start + 1; 1017 while (end < s.size() && s[end] != ':') end++; 1018 std::string arg = s.substr(start, end - start); 1019 size_t doc_start = end + 1; 1020 while (end < s.size() && s[end] != '\n') end++; 1021 std::string ret = s.substr(doc_start, end - doc_start); 1022 replaceArgsRST(ret); 1023 s = s.substr(0, n) + s.substr(end, std::string::npos); 1024 return make_pair(arg, ret); 1025 } 1026 1027public: 1028 PrintRSTVisitor(EnvI& env0, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, 1029 bool includeStdLib) 1030 : env(env0), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} 1031 bool enterModel(Model* m) { 1032 if (!_includeStdLib && m->filename() == "stdlib.mzn") return false; 1033 const std::string& dc = m->docComment(); 1034 if (!dc.empty()) { 1035 size_t gpos = dc.find("@groupdef"); 1036 while (gpos != std::string::npos) { 1037 size_t start = gpos; 1038 while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') start++; 1039 while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) start++; 1040 size_t end = start + 1; 1041 while (end < dc.size() && (isalnum(dc[end]) || dc[end] == '_' || dc[end] == '.')) end++; 1042 std::string groupName = dc.substr(start, end - start); 1043 size_t doc_start = end + 1; 1044 while (end < dc.size() && dc[end] != '\n') end++; 1045 std::string groupHTMLName = dc.substr(doc_start, end - doc_start); 1046 1047 size_t next = dc.find("@groupdef", gpos + 1); 1048 HtmlDocOutput::setGroupDesc( 1049 _maingroup, groupName, groupHTMLName, 1050 (dc.substr(end, next == std::string::npos ? next : next - end))); 1051 gpos = next; 1052 } 1053 } 1054 return true; 1055 } 1056 /// Visit variable declaration 1057 void vVarDeclI(VarDeclI* vdi) { 1058 if (Call* docstring = Expression::dyn_cast<Call>( 1059 getAnnotation(vdi->e()->ann(), constants().ann.doc_comment))) { 1060 std::string ds = eval_string(env, docstring->arg(0)); 1061 std::string group("main"); 1062 size_t group_idx = ds.find("@group"); 1063 if (group_idx != std::string::npos) { 1064 group = HtmlDocOutput::extractArgWord(ds, group_idx); 1065 } 1066 std::ostringstream os; 1067 std::string sig = vdi->e()->type().toString(env) + " " + vdi->e()->id()->str().str(); 1068 1069 std::string myMainGroup = group.substr(0, group.find_first_of(".")); 1070 auto it = _maingroup.subgroups.find(myMainGroup); 1071 os << ".. index::\n"; 1072 if (it != _maingroup.subgroups.m.end()) { 1073 os << " pair: " << (*it)->htmlName << "; " << *vdi->e()->id() << "\n\n"; 1074 } else { 1075 std::cerr << "did not find " << myMainGroup << "\n"; 1076 os << " single: " << *vdi->e()->id() << "\n\n"; 1077 } 1078 1079 os << ".. code-block:: minizinc\n\n"; 1080 if (vdi->e()->ti()->type() == Type::ann()) { 1081 os << " annotation " << *vdi->e()->id(); 1082 } else { 1083 os << " " << *vdi->e()->ti() << ": " << *vdi->e()->id(); 1084 } 1085 os << "\n\n"; 1086 os << HtmlDocOutput::trim(ds) << "\n\n"; 1087 GCLock lock; 1088 HtmlDocOutput::DocItem di( 1089 vdi->e()->type().ispar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, 1090 sig, sig, os.str()); 1091 HtmlDocOutput::addToGroup(_maingroup, group, di); 1092 } 1093 } 1094 /// Visit function item 1095 void vFunctionI(FunctionI* fi) { 1096 if (Call* docstring = 1097 Expression::dyn_cast<Call>(getAnnotation(fi->ann(), constants().ann.doc_comment))) { 1098 std::string ds = eval_string(env, docstring->arg(0)); 1099 std::string group("main"); 1100 size_t group_idx = ds.find("@group"); 1101 if (group_idx != std::string::npos) { 1102 group = HtmlDocOutput::extractArgWord(ds, group_idx); 1103 } 1104 1105 size_t param_idx = ds.find("@param"); 1106 std::vector<std::pair<std::string, std::string> > params; 1107 while (param_idx != std::string::npos) { 1108 params.push_back(extractArgLine(ds, param_idx)); 1109 param_idx = ds.find("@param"); 1110 } 1111 1112 std::vector<std::string> args = replaceArgsRST(ds); 1113 1114 std::unordered_set<std::string> allArgs; 1115 for (unsigned int i = 0; i < args.size(); i++) allArgs.insert(args[i]); 1116 for (unsigned int i = 0; i < params.size(); i++) allArgs.insert(params[i].first); 1117 1118 GCLock lock; 1119 for (unsigned int i = 0; i < fi->params().size(); i++) { 1120 if (allArgs.find(fi->params()[i]->id()->str().str()) == allArgs.end()) { 1121 std::cerr << "Warning: parameter " << *fi->params()[i]->id() 1122 << " not documented for function " << fi->id() << " at location " << fi->loc() 1123 << "\n"; 1124 } 1125 } 1126 1127 std::string sig; 1128 { 1129 GCLock lock; 1130 FunctionI* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); 1131 std::ostringstream oss_sig; 1132 oss_sig << *fi_c; 1133 sig = oss_sig.str(); 1134 sig.resize(sig.size() - 2); 1135 } 1136 1137 std::ostringstream os; 1138 std::ostringstream fs; 1139 std::string myMainGroup = group.substr(0, group.find_first_of(".")); 1140 auto it = _maingroup.subgroups.find(myMainGroup); 1141 os << ".. index::\n"; 1142 if (it != _maingroup.subgroups.m.end()) { 1143 os << " pair: " << (*it)->htmlName << "; " << fi->id() << "\n\n"; 1144 } else { 1145 std::cerr << "did not find " << myMainGroup << "\n"; 1146 os << " single: " << fi->id() << "\n\n"; 1147 } 1148 os << ".. code-block:: minizinc\n\n"; 1149 1150 if (fi->ti()->type() == Type::ann()) { 1151 fs << "annotation "; 1152 } else if (fi->ti()->type() == Type::parbool()) { 1153 fs << "test "; 1154 } else if (fi->ti()->type() == Type::varbool()) { 1155 fs << "predicate "; 1156 } else { 1157 fs << "function " << *fi->ti() << ": "; 1158 } 1159 fs << fi->id() << "("; 1160 os << " " << fs.str(); 1161 size_t align = fs.str().size(); 1162 for (unsigned int i = 0; i < fi->params().size(); i++) { 1163 fs << *fi->params()[i]->ti(); 1164 std::ostringstream fid; 1165 fid << *fi->params()[i]->id(); 1166 if (fid.str().size() != 0) fs << ": " << *fi->params()[i]->id(); 1167 if (i < fi->params().size() - 1) { 1168 fs << ", "; 1169 } 1170 } 1171 bool splitArgs = (fs.str().size() > 70); 1172 for (unsigned int i = 0; i < fi->params().size(); i++) { 1173 os << *fi->params()[i]->ti(); 1174 std::ostringstream fid; 1175 fid << *fi->params()[i]->id(); 1176 if (fid.str().size() != 0) os << ": " << *fi->params()[i]->id(); 1177 if (i < fi->params().size() - 1) { 1178 os << ","; 1179 if (splitArgs) { 1180 os << "\n "; 1181 for (unsigned int j = static_cast<unsigned int>(align); j--;) os << " "; 1182 } else { 1183 os << " "; 1184 } 1185 } 1186 } 1187 os << ")"; 1188 1189 os << "\n\n"; 1190 1191 if (fi->id().c_str()[0] == '\'') { 1192 std::string op = fi->id().str(); 1193 op = op.substr(1, op.length() - 2); 1194 if (fi->params().size() == 2) { 1195 os << "Usage: ``" << *fi->params()[0]->id() << " " << op << " " << *fi->params()[1]->id() 1196 << "``\n\n"; 1197 } else if (fi->params().size() == 1) { 1198 os << "Usage: ``" << op << " " << *fi->params()[0]->id() << "``\n\n"; 1199 } 1200 } 1201 1202 os << HtmlDocOutput::trim(ds) << "\n\n"; 1203 1204 if (fi->e()) { 1205 FunctionI* f_body = fi; 1206 bool alias; 1207 do { 1208 alias = false; 1209 Call* c = Expression::dyn_cast<Call>(f_body->e()); 1210 if (c && c->n_args() == f_body->params().size()) { 1211 bool sameParams = true; 1212 for (unsigned int i = 0; i < f_body->params().size(); i++) { 1213 Id* ident = c->arg(i)->dyn_cast<Id>(); 1214 if (ident == NULL || ident->decl() != f_body->params()[i] || 1215 ident->str() != c->decl()->params()[i]->id()->str()) { 1216 sameParams = false; 1217 break; 1218 } 1219 } 1220 if (sameParams) { 1221 alias = true; 1222 f_body = c->decl(); 1223 } 1224 } 1225 } while (alias); 1226 if (f_body->e()) { 1227 std::string filename = f_body->loc().filename().str(); 1228 size_t filePos = filename.find("std/"); 1229 if (filePos != std::string::npos) { 1230 filePos += 4; 1231 os << ".. only:: builder_html\n\n"; 1232 os << " `More... <https://github.com/MiniZinc/libminizinc/blob/" << MZN_VERSION_MAJOR 1233 << "." << MZN_VERSION_MINOR << "." << MZN_VERSION_PATCH << "/share/minizinc/std/" 1234 << filename.substr(filePos, std::string::npos) << "#L" << f_body->loc().first_line() 1235 << "-L" << f_body->loc().last_line() << ">`__\n\n"; 1236 } 1237 } 1238 } 1239 1240 if (params.size() > 0) { 1241 os << "Parameters:\n\n"; 1242 for (unsigned int i = 0; i < params.size(); i++) { 1243 os << "- ``" << params[i].first << "``: " << params[i].second << "\n"; 1244 } 1245 os << "\n"; 1246 } 1247 os << "\n"; 1248 1249 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, fi->id().str(), sig, os.str()); 1250 HtmlDocOutput::addToGroup(_maingroup, group, di); 1251 } 1252 } 1253}; 1254 1255std::vector<HtmlDocument> RSTPrinter::printRST(EnvI& env, MiniZinc::Model* m, 1256 const std::string& basename, int splitLevel, 1257 bool includeStdLib, bool generateIndex) { 1258 using namespace HtmlDocOutput; 1259 Group g(basename, basename); 1260 FunMap funMap; 1261 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 1262 iterItems(fv, m); 1263 PrintRSTVisitor prv(env, g, funMap, includeStdLib); 1264 iterItems(prv, m); 1265 1266 std::vector<HtmlDocument> ret; 1267 1268 std::ostringstream oss; 1269 oss << Group::rstHeading(g.htmlName, 0); 1270 oss << trim(g.desc) << "\n"; 1271 oss << ".. toctree::\n\n"; 1272 for (auto sg : g.subgroups.m) { 1273 oss << " " << sg->fullPath << "\n"; 1274 } 1275 1276 ret.push_back(HtmlDocument(g.fullPath, g.htmlName, oss.str())); 1277 1278 for (auto& sg : g.subgroups.m) { 1279 ret.push_back(HtmlDocument(sg->fullPath, sg->htmlName, sg->toRST(0))); 1280 } 1281 return ret; 1282} 1283 1284} // namespace MiniZinc