2026-09-08 16:54:21 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
#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();
|
2026-09-25 00:32:55 +02:00
|
|
|
return sks_commands.count(filename.string()) == 0;
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-25 22:12:48 +02:00
|
|
|
// "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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 16:54:21 +02:00
|
|
|
std::string Error::message(const std::string& epilog) const
|
2026-07-18 18:48:23 +02:00
|
|
|
{
|
2026-08-16 01:37:59 +02:00
|
|
|
// 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.
|
2026-09-08 16:54:21 +02:00
|
|
|
std::string desc = m_desc;
|
2026-08-16 01:37:59 +02:00
|
|
|
if (epilog != "") {
|
2026-09-08 16:54:21 +02:00
|
|
|
desc += "\n\n" + epilog + "\n";
|
2026-08-16 01:37:59 +02:00
|
|
|
}
|
2026-09-08 16:54:21 +02:00
|
|
|
std::stringstream ss {};
|
|
|
|
|
ss << command_name << " (" << m_type << " error)";
|
2026-09-25 22:12:48 +02:00
|
|
|
// 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;
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
2026-09-25 22:12:48 +02:00
|
|
|
if (head.m_chr > -1) {
|
|
|
|
|
ss << ", character " << head.m_chr + 1;
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
2026-09-25 22:12:48 +02:00
|
|
|
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";
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
2026-09-25 22:12:48 +02:00
|
|
|
ss << justify(desc, 80, 0);
|
2026-09-08 16:54:21 +02:00
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Error::print_message(const std::string& epilog)
|
|
|
|
|
{
|
|
|
|
|
std::cerr << "\n" << red << message(epilog) << reset << "\n";
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|