diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/ansicolor.hh | 13 | ||||
-rw-r--r-- | src/libutil/error.cc | 146 | ||||
-rw-r--r-- | src/libutil/error.hh | 121 | ||||
-rw-r--r-- | src/libutil/types.hh | 10 | ||||
-rw-r--r-- | src/libutil/util.hh | 11 |
5 files changed, 288 insertions, 13 deletions
diff --git a/src/libutil/ansicolor.hh b/src/libutil/ansicolor.hh new file mode 100644 index 000000000..390bd4d17 --- /dev/null +++ b/src/libutil/ansicolor.hh @@ -0,0 +1,13 @@ +#pragma once + +namespace nix +{ + /* Some ANSI escape sequences. */ + #define ANSI_NORMAL "\e[0m" + #define ANSI_BOLD "\e[1m" + #define ANSI_FAINT "\e[2m" + #define ANSI_RED "\e[31;1m" + #define ANSI_GREEN "\e[32;1m" + #define ANSI_YELLOW "\e[33;1m" + #define ANSI_BLUE "\e[34;1m" +} diff --git a/src/libutil/error.cc b/src/libutil/error.cc new file mode 100644 index 000000000..a5571d4ec --- /dev/null +++ b/src/libutil/error.cc @@ -0,0 +1,146 @@ +#include "error.hh" + +#include <iostream> +#include <optional> + +namespace nix +{ + +std::optional<string> ErrorInfo::programName = std::nullopt; + +std::ostream& operator<<(std::ostream &os, const hintformat &hf) +{ + return os << hf.str(); +} + +string showErrPos(const ErrPos &errPos) +{ + if (errPos.column > 0) { + return fmt("(%1%:%2%)", errPos.lineNumber, errPos.column); + } else { + return fmt("(%1%)", errPos.lineNumber); + }; +} + +void printCodeLines(const string &prefix, const NixCode &nixCode) +{ + // previous line of code. + if (nixCode.prevLineOfCode.has_value()) { + std::cout << fmt("%1% %|2$5d|| %3%", + prefix, + (nixCode.errPos.lineNumber - 1), + *nixCode.prevLineOfCode) + << std::endl; + } + + // line of code containing the error.%2$+5d% + std::cout << fmt("%1% %|2$5d|| %3%", + prefix, + (nixCode.errPos.lineNumber), + nixCode.errLineOfCode) + << std::endl; + + // error arrows for the column range. + if (nixCode.errPos.column > 0) { + int start = nixCode.errPos.column; + std::string spaces; + for (int i = 0; i < start; ++i) { + spaces.append(" "); + } + + std::string arrows("^"); + + std::cout << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL, + prefix, + spaces, + arrows) << std::endl; + } + + // next line of code. + if (nixCode.nextLineOfCode.has_value()) { + std::cout << fmt("%1% %|2$5d|| %3%", + prefix, + (nixCode.errPos.lineNumber + 1), + *nixCode.nextLineOfCode) + << std::endl; + } +} + +void printErrorInfo(const ErrorInfo &einfo) +{ + int errwidth = 80; + string prefix = " "; + + string levelString; + switch (einfo.level) { + case ErrLevel::elError: { + levelString = ANSI_RED; + levelString += "error:"; + levelString += ANSI_NORMAL; + break; + } + case ErrLevel::elWarning: { + levelString = ANSI_YELLOW; + levelString += "warning:"; + levelString += ANSI_NORMAL; + break; + } + default: { + levelString = fmt("invalid error level: %1%", einfo.level); + break; + } + } + + int ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length(); + int dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl; + + string dashes; + for (int i = 0; i < dashwidth; ++i) + dashes.append("-"); + + // divider. + std::cout << fmt("%1%%2%" ANSI_BLUE " %3% %4% %5% %6%" ANSI_NORMAL, + prefix, + levelString, + "---", + einfo.name, + dashes, + einfo.programName.value_or("")) + << std::endl; + + // filename. + if (einfo.nixCode.has_value()) { + if (einfo.nixCode->errPos.nixFile != "") { + string eline = einfo.nixCode->errLineOfCode != "" + ? string(" ") + showErrPos(einfo.nixCode->errPos) + : ""; + + std::cout << fmt("%1%in file: " ANSI_BLUE "%2%%3%" ANSI_NORMAL, + prefix, + einfo.nixCode->errPos.nixFile, + eline) << std::endl; + std::cout << prefix << std::endl; + } else { + std::cout << fmt("%1%from command line argument", prefix) << std::endl; + std::cout << prefix << std::endl; + } + } + + // description + std::cout << prefix << einfo.description << std::endl; + std::cout << prefix << std::endl; + + // lines of code. + if (einfo.nixCode->errLineOfCode != "") { + printCodeLines(prefix, *einfo.nixCode); + std::cout << prefix << std::endl; + } + + // hint + if (einfo.hint.has_value()) { + std::cout << prefix << *einfo.hint << std::endl; + std::cout << prefix << std::endl; + } +} + +} diff --git a/src/libutil/error.hh b/src/libutil/error.hh new file mode 100644 index 000000000..f402b692e --- /dev/null +++ b/src/libutil/error.hh @@ -0,0 +1,121 @@ +#ifndef error_hh +#define error_hh + +#include "ansicolor.hh" +#include <string> +#include <optional> +#include <iostream> +#include "types.hh" + +namespace nix +{ + +typedef enum { + elWarning, + elError +} ErrLevel; + +struct ErrPos +{ + int lineNumber; + int column; + string nixFile; + + template <class P> + ErrPos& operator=(const P &pos) + { + lineNumber = pos.line; + column = pos.column; + nixFile = pos.file; + return *this; + } + + template <class P> + ErrPos(const P &p) + { + *this = p; + } +}; + +struct NixCode +{ + ErrPos errPos; + std::optional<string> prevLineOfCode; + string errLineOfCode; + std::optional<string> nextLineOfCode; +}; + +// ---------------------------------------------------------------- +// format function for hints. same as fmt, except templated values +// are always in yellow. + +template <class T> +struct yellowify +{ + yellowify(T &s) : value(s) {} + T &value; +}; + +template <class T> +std::ostream& operator<<(std::ostream &out, const yellowify<T> &y) +{ + return out << ANSI_YELLOW << y.value << ANSI_NORMAL; +} + +class hintformat +{ +public: + hintformat(string format) :fmt(format) + { + fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); + } + template<class T> + hintformat& operator%(const T &value) + { + fmt % yellowify(value); + return *this; + } + + std::string str() const + { + return fmt.str(); + } + + template <typename U> + friend class AddHint; +private: + format fmt; +}; + +std::ostream& operator<<(std::ostream &os, const hintformat &hf); + +template<typename... Args> +inline hintformat hintfmt(const std::string & fs, const Args & ... args) +{ + hintformat f(fs); + formatHelper(f, args...); + return f; +} + +// ------------------------------------------------- +// ErrorInfo. +struct ErrorInfo +{ + ErrLevel level; + string name; + string description; + std::optional<hintformat> hint; + std::optional<NixCode> nixCode; + + static std::optional<string> programName; +}; + +// -------------------------------------------------------- +// error printing + +// just to cout for now. +void printErrorInfo(const ErrorInfo &einfo); + +} + +#endif diff --git a/src/libutil/types.hh b/src/libutil/types.hh index a1ce7b372..250c9581d 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -41,7 +41,8 @@ struct FormatOrString { string s; FormatOrString(const string & s) : s(s) { }; - FormatOrString(const format & f) : s(f.str()) { }; + template<class F> + FormatOrString(const F & f) : s(f.str()) { }; FormatOrString(const char * s) : s(s) { }; }; @@ -51,12 +52,13 @@ struct FormatOrString ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion takes place). */ -inline void formatHelper(boost::format & f) +template<class F> +inline void formatHelper(F & f) { } -template<typename T, typename... Args> -inline void formatHelper(boost::format & f, const T & x, const Args & ... args) +template<class F, typename T, typename... Args> +inline void formatHelper(F & f, const T & x, const Args & ... args) { formatHelper(f % x, args...); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 815b1f288..8e24ef968 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -2,6 +2,8 @@ #include "types.hh" #include "logging.hh" +#include "ansicolor.hh" +#include "error.hh" #include <sys/types.h> #include <sys/stat.h> @@ -446,15 +448,6 @@ std::string shellEscape(const std::string & s); void ignoreException(); -/* Some ANSI escape sequences. */ -#define ANSI_NORMAL "\e[0m" -#define ANSI_BOLD "\e[1m" -#define ANSI_FAINT "\e[2m" -#define ANSI_RED "\e[31;1m" -#define ANSI_GREEN "\e[32;1m" -#define ANSI_YELLOW "\e[33;1m" -#define ANSI_BLUE "\e[34;1m" - /* Tree formatting. */ constexpr char treeConn[] = "├───"; |