alec

Abstraction Layer for Escape Codes
git clone git://git.dimitrijedobrota.com/alec.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commitc63bca584930693f0470ee517f924d3eedccabb7
parent621cc1fdfe336bbff877ceac0318e289d2eb811f
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateMon, 1 Jan 2024 23:18:06 +0000

Rewrite, use template variables * Not locked in with basic_ostream * Greatly simplify the template code * Cleaner implementation * Enforce validity with concepts * Further improved my understanding of templates * Add details namespace to hide implementation

Diffstat:
MCMakeLists.txt|+-
Mdemo/demo.cpp|++--
Msrc/alec.hpp|++++++++++++++++++++++++++++++++--------------------------------------------------

3 files changed, 127 insertions(+), 195 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
Alec
VERSION 0.0.4
VERSION 0.0.5
DESCRIPTION "Abstraction Layer for Escape Codes"
HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git
LANGUAGES CXX

diff --git a/demo/demo.cpp b/demo/demo.cpp

@@ -7,10 +7,10 @@ int main(void) {

std::cout << ABUF_SHOW << CURSOR_HIDE;
std::cout << CURSOR_POSITION<1, 1> << FOREGROUND<91> << "HELLO!\n";
std::cout << CURSOR_POSITION<1, 1> << FOREGROUND_256<91> << "HELLO!\n";
std::cout << CURSOR_DOWN<3>;
std::cout << FOREGROUND<30> << BACKGROUND<196> << "WORLD!\n";
std::cout << FOREGROUND_256<30> << BACKGROUND_256<196> << "WORLD!\n";
std::cout << BACKGROUND<DEFAULT> << "testing 1...\n" << FOREGROUND<DEFAULT>;
std::cout << DECOR_SET<INVERSE> << "testing 2...\n" << DECOR_RESET<INVERSE>;

diff --git a/src/alec.hpp b/src/alec.hpp

@@ -1,10 +1,11 @@

#ifndef ALEC_ALEC_H
#define ALEC_ALEC_H
#include <algorithm>
#include <array>
#include <cstdint>
#include <iostream>
#include <string_view>
#include <type_traits>
namespace ALEC {

@@ -47,233 +48,164 @@ enum class DECOR {

using enum COLOR;
using enum DECOR;
template <std::string_view const &...Strs> struct join {
static constexpr auto arr = []() {
constexpr std::size_t len = (Strs.size() + ... + 0);
std::array<char, len + 1> arr{};
auto append = [i = 0, &arr](auto const &s) mutable {
for (auto c : s)
arr[i++] = c;
};
(append(Strs), ...);
namespace details {
template <std::size_t N> struct string_literal {
consteval string_literal(const char (&str)[N]) { std::copy_n(str, N, value); }
consteval std::size_t size() const { return N; }
char value[N];
};
template <auto... Args> struct escape_t {
template <typename T> static consteval std::size_t size(T val);
template <typename T> static constexpr char *append(char *ptr, T val);
template <std::size_t N> static constexpr std::size_t size(string_literal<N> val) { return val.size(); }
static constexpr std::size_t size(char val) { return 1; }
static constexpr std::size_t size(intmax_t val) {
std::size_t len = 1;
while (val /= 10)
len++;
return len;
}
template <std::size_t N> static constexpr char *append(char *ptr, string_literal<N> val) {
std::copy_n(val.value, N, ptr);
return ptr + N;
}
static constexpr char *append(char *ptr, char val) {
*ptr++ = val;
return ptr;
}
static constexpr char *append(char *ptr, intmax_t val) {
char *tmp = ptr += size(val);
do {
*--tmp = '0' + (val % 10);
} while (val /= 10);
return ptr;
}
static constexpr const auto value = []() {
constexpr std::size_t len = (size(Args) + ... + 2);
std::array<char, len + 1> arr{CTRL::ESC, '[', 0};
auto map = [ptr = arr.data() + 2](auto const &s) mutable { ptr = append(ptr, s); };
(map(Args), ...);
arr[len] = 0;
return arr;
}();
static constexpr std::string_view value{arr.data(), arr.size() - 1};
};
template <std::intmax_t N> class to_string_t {
public:
constexpr operator const auto &() const { return value; }
private:
static constexpr auto buf = []() {
constexpr const auto len = []() {
std::size_t len = 1;
for (auto n = N; n; len++, n /= 10)
;
return len;
}();
template <auto... Strs> static constexpr auto escape = escape_t<Strs...>::value.data();
std::array<char, len> arr{};
} // namespace details
auto ptr = arr.data() + len;
*--ptr = '\0';
// Tamplate parameter constraints
arr[0] = 0;
for (auto n = N; n; n /= 10)
*--ptr = '0' + n % 10;
template <intmax_t n>
concept limit_256 = n >= 0 && n < 256;
return arr;
}();
static constexpr std::string_view value{buf.data(), buf.size() - 1};
};
template <intmax_t n>
concept limit_pos = n >= 0;
template <char C> class to_string_c {
public:
constexpr operator const auto &() const { return value; }
private:
static constexpr auto c = C;
static constexpr std::string_view value{&c, 1};
};
template <intmax_t n>
concept limit_4 = n >= 0 && n <= 4;
static constexpr const std::string_view buffer = "?1049";
static constexpr const std::string_view cursor = "?25";
static constexpr const std::string_view esc = "\033[";
static constexpr const std::string_view sep = ";";
// Move cursor up/down/frwd/back
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_UP = details::escape<n, 'A'>;
template <std::string_view const &...Strs> static constexpr auto concatenate = join<Strs...>::value;
template <std::string_view const &...Strs> static constexpr auto escape = join<esc, Strs...>::value;
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_DOWN = details::escape<n, 'B'>;
template <std::intmax_t N> constexpr to_string_t<N> to_string;
template <char C> constexpr to_string_c<C> to_stringc;
template <std::intmax_t N> static constexpr auto to_stringp = join<to_string<N>, sep>::value;
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_FRWD = details::escape<n, 'C'>;
// Move cursor up/down/frwd/back
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_UP(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'A'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_DOWN(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'B'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_FRWD(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'C'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_BACK(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'D'>>;
}
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_BACK = details::escape<n, 'D'>;
// Move cursor to the next/prev line
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_LINE_NEXT(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'E'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_LINE_PREV(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'F'>>;
}
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_LINE_NEXT = details::escape<n, 'E'>;
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_LINE_PREV = details::escape<n, 'F'>;
// Set cursor to specific column
template <int n, class CharT, class Traits>
static constexpr auto &CURSOR_COLUMN(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'G'>>;
}
template <intmax_t n>
requires limit_pos<n>
static constexpr auto CURSOR_COLUMN = details::escape<n, 'G'>;
// Erase functions
template <int n, class CharT, class Traits>
static constexpr auto &ERASE_DISPLAY(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0 && n <= 4, "invalid argument");
return bos << escape<to_string<n>, to_stringc<'J'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &ERASE_LINE(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0 && n <= 4, "invalid argument");
return bos << escape<to_string<n>, to_stringc<'K'>>;
}
template <intmax_t n>
requires limit_4<n>
static constexpr auto ERASE_DISPLAY = details::escape<n, 'J'>;
template <intmax_t n>
requires limit_4<n>
static constexpr auto ERASE_LINE = details::escape<n, 'K'>;
// Scroll up/down
template <int n, class CharT, class Traits>
static constexpr auto &SCROLL_UP(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'S'>>;
}
template <int n, class CharT, class Traits>
static constexpr auto &SCROLL_DOWN(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0, "cant' use negative numbers");
return bos << escape<to_string<n>, to_stringc<'T'>>;
}
template <intmax_t n>
requires limit_pos<n>
static constexpr auto SCROLL_UP = details::escape<n, 'S'>;
template <intmax_t n>
requires limit_pos<n>
static constexpr auto SCROLL_DOWN = details::escape<n, 'T'>;
// Set cursor to a specific position
template <int n, int m, class CharT, class Traits>
static constexpr auto &CURSOR_POSITION(std::basic_ostream<CharT, Traits> &bos) {
static_assert(n >= 0 && m >= 0, "cant' use negative numbers");
return bos << escape<to_stringp<n>, to_string<m>, to_stringc<'H'>>;
}
template <intmax_t n, intmax_t m>
requires limit_pos<n> && limit_pos<m>
static constexpr auto CURSOR_POSITION = details::escape<n, ';', m, 'H'>;
// palet colors
template <COLOR color, class CharT, class Traits>
static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_string<static_cast<uint>(color) + 30>, to_stringc<'m'>>;
}
template <COLOR color, class CharT, class Traits>
static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_string<static_cast<uint>(color) + 40>, to_stringc<'m'>>;
}
template <COLOR color> static constexpr auto FOREGROUND = details::escape<(intmax_t)color + 30, 'm'>;
template <COLOR color> static constexpr auto BACKGROUND = details::escape<(intmax_t)color + 40, 'm'>;
// 256-color palette
template <int idx, class CharT, class Traits>
static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) {
static_assert(idx >= 0 && idx < 256, "value must be between 0 and 255");
return bos << escape<to_stringp<38>, to_stringp<5>, to_string<idx>, to_stringc<'m'>>;
}
template <int idx, class CharT, class Traits>
static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) {
static_assert(idx >= 0 && idx < 256, "value must be between 0 and 255");
return bos << escape<to_stringp<48>, to_stringp<5>, to_string<idx>, to_stringc<'m'>>;
}
template <intmax_t idx>
requires limit_256<idx>
static constexpr auto FOREGROUND_256 = details::escape<intmax_t(38), ';', intmax_t(5), ';', idx, 'm'>;
template <intmax_t idx>
requires limit_256<idx>
static constexpr auto BACKGROUND_256 = details::escape<intmax_t(48), ';', intmax_t(5), ';', idx, 'm'>;
// RGB colors
template <int R, int G, int B, class CharT, class Traits>
static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) {
static_assert(R >= 0 && R < 256, "R value must be between 0 and 255");
static_assert(G >= 0 && G < 256, "G value must be between 0 and 255");
static_assert(B >= 0 && B < 256, "B value must be between 0 and 255");
return bos << escape<to_stringp<38>, to_stringp<5>, to_stringp<R>, to_stringp<G>, to_string<B>,
to_stringc<'m'>>;
}
template <int R, int G, int B, class CharT, class Traits>
static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) {
static_assert(R >= 0 && R < 256, "R value must be between 0 and 255");
static_assert(G >= 0 && G < 256, "G value must be between 0 and 255");
static_assert(B >= 0 && B < 256, "B value must be between 0 and 255");
return bos << escape<to_stringp<48>, to_stringp<5>, to_stringp<R>, to_stringp<G>, to_string<B>,
to_stringc<'m'>>;
}
template <intmax_t R, intmax_t G, intmax_t B>
requires limit_256<R> && limit_256<G> && limit_256<B>
static constexpr auto FOREGROUND_RGB =
details::escape<intmax_t(38), ';', intmax_t(5), ';', R, ';', G, ';', B, 'm'>;
template <intmax_t R, intmax_t G, intmax_t B>
requires limit_256<R> && limit_256<G> && limit_256<B>
static constexpr auto BACKGROUND_RGB =
details::escape<intmax_t(48), ';', intmax_t(5), ';', R, ';', G, ';', B, 'm'>;
// Set/reset text decorators
template <DECOR decor, class CharT, class Traits>
static constexpr auto &DECOR_SET(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_string<static_cast<uint>(decor)>, to_stringc<'m'>>;
}
template <DECOR decor, class CharT, class Traits>
static constexpr auto &DECOR_RESET(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_string<static_cast<uint>(decor) + 20>, to_stringc<'m'>>;
}
// Savle/load cursor position
template <class CharT, class Traits>
static constexpr auto &CURSOR_SAVE(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_stringc<'s'>>;
}
template <class CharT, class Traits>
static constexpr auto &CURSOR_LOAD(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<to_stringc<'u'>>;
}
template <DECOR decor> static constexpr const char *DECOR_SET = details::escape<(intmax_t)decor, 'm'>;
template <DECOR decor> static constexpr const char *DECOR_RESET = details::escape<(intmax_t)decor + 20, 'm'>;
// Show/hide cursor
template <class CharT, class Traits>
static constexpr auto &CURSOR_SHOW(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<cursor, to_stringc<'h'>>;
}
// Savle/load cursor position;
static constexpr const char *CURSOR_SAVE = details::escape<'s'>;
static constexpr const char *CURSOR_LOAD = details::escape<'u'>;
template <class CharT, class Traits>
static constexpr auto &CURSOR_HIDE(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<cursor, to_stringc<'l'>>;
}
// Show/hide cursor
static constexpr const char *CURSOR_SHOW = details::escape<details::string_literal("?25h")>;
static constexpr const char *CURSOR_HIDE = details::escape<details::string_literal("?25l")>;
// Show/hide alternate buffer
template <class CharT, class Traits>
static constexpr auto &ABUF_SHOW(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<buffer, to_stringc<'h'>>;
}
template <class CharT, class Traits>
static constexpr auto &ABUF_HIDE(std::basic_ostream<CharT, Traits> &bos) {
return bos << escape<buffer, to_stringc<'l'>>;
}
static constexpr const char *ABUF_SHOW = details::escape<details::string_literal("?1049h")>;
static constexpr const char *ABUF_HIDE = details::escape<details::string_literal("?1049l")>;
} // namespace ALEC