#include "section_link.h" #include "error.h" #include "locator.h" #include "util.h" #include #include std::string normalize_section_title(const std::string& s) { static const std::regex number_rgx(R"(.*?)"); static const std::regex tag_rgx("<[^>]*>"); static const std::regex tex_command_rgx(R"(\\[A-Za-z@]+\*?)"); static const std::regex space_rgx(R"(\s+)"); std::string t = std::regex_replace(s, number_rgx, ""); t = std::regex_replace(t, tag_rgx, ""); t = std::regex_replace(t, tex_command_rgx, ""); t = string_replace(string_replace(t, "{", ""), "}", ""); t = std::regex_replace(t, space_rgx, " "); return trim(t); } Section_target resolve_section_target( const std::vector& table, const std::string& title, const std::string& file, const std::string& where, const std::vector& files) { const std::string wanted = normalize_section_title(title); const std::string in = file.empty() ? where : file; // the one file searched const std::string in_name = in.empty() ? std::string("the document's :text") : in; std::stringstream ss {}; ss << "In " << (where.empty() ? std::string("the document's :text") : where) << ", a :section link names the heading " << q_(title); if (!file.empty()) { bool known = false; for (const auto& f : files) known = known || f == file; if (!known) { ss << " in " << file << ", but the document has no file " << file << " (its files are " << join(files, ", ") << ")."; throw Argument_error(ss.str(), Locator::none()); } } std::vector hits {}; for (const auto& t : table) { if (t.basename == in && t.title == wanted) hits.push_back(&t); } if (hits.size() == 1) return *hits[0]; if (hits.empty()) { ss << ", which " << in_name << " does not have" << (file.empty() ? " (a section in another file needs its basename as the value of :section)." : "."); } else { ss << ", which " << in_name << " has " << hits.size() << " times: give one of them an :id and link to that."; } throw Argument_error(ss.str(), Locator::none()); }