#include #include "error.h" #include "show.h" #include "util.h" #include "file.h" bool display_source(const std::string& filename_arg) { if (filename_arg.empty()) { return false; } else { fs::path filename = fs::path(filename_arg).filename(); return sks_commands.count(filename.string()) == 0; } } // "file, line N, character M" for a location, under the same rule as the // headline: the file only when display_source() says so. static std::string where(const Locator& loc) { std::stringstream ss {}; if (display_source(loc.m_filename)) { ss << loc.m_filename; } if (loc.m_line > -1) { ss << (ss.str().empty() ? "line " : ", line ") << loc.m_line; } if (loc.m_chr > -1) { ss << ", character " << loc.m_chr + 1; } return ss.str(); } std::string Error::message(const std::string& epilog) const { // The separator is not cosmetic: the description ends with a sentence, and // appending the advice straight onto it ran the two together into one word // ("...for an unspecified targetkdiag loads no klammerset..."). m_desc is // then justified to 80 columns, so the break has to be a blank line rather // than a single newline, which justify() would fold back into the flow. std::string desc = m_desc; if (epilog != "") { desc += "\n\n" + epilog + "\n"; } std::stringstream ss {}; ss << command_name << " (" << m_type << " error)"; // The headline is location(): the outermost application when the error // escaped through klammer applications, which is the user's document. const Locator& head = location(); if (display_source(head.m_filename)) { ss << ":\n " << head.m_filename; } if (head.m_line > -1) { ss << ", line " << head.m_line; } if (head.m_chr > -1) { ss << ", character " << head.m_chr + 1; } ss << ":\n\n"; // Then the chain, outermost first, each level saying which klammer was // applied and, below the top, where; last, where the error itself was // raised when that is not the innermost application. Lines begin with // two spaces so justify() keeps them verbatim. A recursion error // carries hundreds of levels: the outermost, an ellipsis and the two // innermost say what a person needs. if (!m_applications.empty()) { const std::size_t n = m_applications.size(); auto level = [&](std::size_t i, bool with_place) { // i: 0 = innermost ss << " applying @" << m_applications[i].first; if (with_place) { ss << " at " << where(m_applications[i].second); } ss << "\n"; }; level(n - 1, false); if (n > 4) { ss << " ... " << n - 3 << " applications ...\n"; level(1, true); level(0, true); } else { for (std::size_t i = n - 1; i-- > 0; ) { level(i, true); } } const Locator& inner = m_applications.front().second; if (m_loc.m_filename != inner.m_filename || m_loc.m_line != inner.m_line || m_loc.m_chr != inner.m_chr) { ss << " at " << where(m_loc) << "\n"; } ss << "\n"; } ss << justify(desc, 80, 0); return ss.str(); } void Error::print_message(const std::string& epilog) { std::cerr << "\n" << red << message(epilog) << reset << "\n"; }