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

commit c63bca584930693f0470ee517f924d3eedccabb7
parent 621cc1fdfe336bbff877ceac0318e289d2eb811f
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Mon, 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:
M CMakeLists.txt | + -
M demo/demo.cpp | ++ --
M src/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