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 e584d30759cc8d1233906f107c9c2e07683a66ca
parent b02e0ba34e3a27238bfa660bedf1e87fb6313f15
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Sun, 15 Jun 2025 18:18:50 +0200

Greatly simplify the implementation

Diffstat:
M CMakeLists.txt | + -
M example/alec_runtime.cpp | + -
M include/alec/alec.hpp | +++++++++++++++++++++++++++++++++++++ ---------------------------------------------

3 files changed, 156 insertions(+), 189 deletions(-)


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

@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)


project(
alec
VERSION 0.2.0
VERSION 0.2.1
DESCRIPTION "Abstraction Layer for Escape Codes"
HOMEPAGE_URL "git://git.dimitrijedobrota.com/alec.git"
LANGUAGES CXX

diff --git a/ example/alec_runtime.cpp b/ example/alec_runtime.cpp

@@ -12,7 +12,7 @@ int main()

std::cout << cursor_position(1, 1) << foreground(91) << "HELLO!\n";

std::cout << cursor_down(3);
std::cout << foreground(30) << background(96, 53, 64) << "WORLD!\n";
std::cout << foreground(30) << background(196, 53, 64) << "WORLD!\n";

std::cout << background(color::def) << "testing 1...\n"
<< foreground(color::def);

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

@@ -1,13 +1,13 @@

#pragma once

#include <algorithm>
#include <array>
#include <cassert>
#include <string>

#include <based/enum/enum.hpp>
#include <based/string/literal.hpp>
#include <based/string/to_string.hpp>
#include <based/types/types.hpp>
#include <based/utility/static_view.hpp>

namespace alec
{

@@ -32,198 +32,63 @@ BASED_DEFINE_ENUM(motion, based::bu8, 0, ENUM_MOTION)

namespace detail
{

template<std::size_t n>
static constexpr std::size_t size(based::string_literal<n> /*val*/)
{
return n;
}
static constexpr std::size_t size(char /*val*/)
{
return 1;
}
static constexpr std::string start = "\x01B[";

template<class T>
static constexpr std::size_t size(T val)
constexpr void append(std::string& res, const auto& arg)
{
std::size_t len = 1;
while ((val /= 10) != 0) {
len++;
}
return len;
res += based::to_string(arg);
res += ';';
}

template<std::size_t n>
static constexpr char* append(char* ptr, based::string_literal<n> val)
template<class... Args>
constexpr std::string test(char chr, Args... args)
{
std::copy_n(val.data(), n, ptr);
return ptr + n; // NOLINT
}
auto res = start;

static constexpr char* append(char* ptr, char val)
{
*ptr++ = val; // NOLINT
return ptr;
}

template<class T>
static constexpr char* append(char* ptr, T val)
{
char* tmp = ptr += size(val); // NOLINT
do { // NOLINT
*--tmp = '0' + static_cast<char>(val % 10); // NOLINT
} while ((val /= 10) != 0);
return ptr;
}
(append(res, args), ...);
res.back() = chr;

static constexpr std::string make(auto... args)
{
std::string res((size(args) + ... + 2), 0);
res[0] = 0x1B, res[1] = '[';
auto* ptr = res.data() + 2; // NOLINT
((ptr = append(ptr, args)), ...);
return res;
}

template<auto... args>
struct escape_t
{
static constexpr const auto value = []()
{
std::array<char, (size(args) + ... + 3)> arr = {};
arr[0] = 0x1B, arr[1] = '[';
auto* ptr = arr.data() + 2;
((ptr = append(ptr, args)), ...);
return arr;
}();
static constexpr auto data = value.data();
};

template<auto... args>
static constexpr auto escape = alec::detail::escape_t<args...>::data;

template<based::string_literal... strs>
static constexpr auto escape_literal = escape<strs...>;
template<auto func, auto... args>
static constexpr std::string_view escape = based::to_string_view(
[]()
{
return func(args...);
}
);

template<based::string_literal str>
static constexpr std::string_view escape_literal = based::to_string_view(
[]
{
return start + std::string(str);
}
);

} // namespace detail

// Template compile-time variables

// Forward-declare templates

template<auto... val>
static const char* const background_v = "";

template<auto... val>
static const char* const foreground_v = "";

// Template specializations

// Move cursor up/down/frwd/back
template<unsigned cnt>
static constexpr auto cursor_up_v = detail::escape<cnt, 'A'>;

template<unsigned cnt>
static constexpr auto cursor_down_v = detail::escape<cnt, 'B'>;

template<unsigned cnt>
static constexpr auto cursor_frwd_v = detail::escape<cnt, 'C'>;

template<unsigned cnt>
static constexpr auto cursor_back_v = detail::escape<cnt, 'D'>;

// Move cursor to the next/prev line
template<unsigned cnt>
static constexpr auto cursor_line_next_v = detail::escape<cnt, 'E'>;

template<unsigned cnt>
static constexpr auto cursor_line_prev_v = detail::escape<cnt, 'F'>;

// Set cursor to specific column
template<unsigned col>
static constexpr auto cursor_column_v = detail::escape<col, 'G'>;

// Erase functions
template<motion::enum_type motion>
static constexpr auto erase_display_v = detail::escape<motion(), 'J'>;

template<motion::enum_type motion>
static constexpr auto erase_line_v = detail::escape<motion(), 'K'>;

// Scroll up/down
template<unsigned cnt>
static constexpr auto scroll_up_v = detail::escape<cnt, 'S'>;

template<unsigned cnt>
static constexpr auto scroll_down_v = detail::escape<cnt, 'T'>;

// Set cursor to a specific position
template<unsigned row, unsigned col>
static constexpr auto cursor_position_v = detail::escape<row, ';', col, 'H'>;

// color
// palet colors
template<color::enum_type color>
static constexpr auto foreground_v<color> = detail::escape<color() + 30, 'm'>;

template<color::enum_type color>
static constexpr auto background_v<color> = detail::escape<color() + 40, 'm'>;

// 256-color palette
template<based::bu8 idx>
static constexpr auto foreground_v<idx> =
detail::escape<38, ';', 5, ';', idx, 'm'>;

template<based::bu8 idx>
static constexpr auto background_v<idx> =
detail::escape<48, ';', 5, ';', idx, 'm'>;

// RGB colors
template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto foreground_v<red, green, blue> =
detail::escape<38, ';', 2, ';', red, ';', green, ';', blue, 'm'>;

template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto background_v<red, green, blue> =
detail::escape<48, ';', 2, ';', red, ';', green, ';', blue, 'm'>;

// Set/reset text decorators
template<decor::enum_type decor>
static constexpr auto decor_set_v = detail::escape<decor(), 'm'>;

template<decor::enum_type decor>
static constexpr auto decor_reset_v = detail::escape<decor() + 20, 'm'>;

// Save/restore cursor position;
static constexpr auto cursor_save_v = detail::escape<'s'>;

static constexpr auto cursor_restore_v = detail::escape<'u'>;

// Set screen modes
template<unsigned mode>
static constexpr auto screen_mode_set_v = detail::escape<'=', mode, 'h'>;

template<unsigned mode>
static constexpr auto screen_mode_reset_v = detail::escape<'=', mode, 'l'>;

// Private screen modes supported by most terminals
// Save/restore screen
static constexpr auto screen_save_v = detail::escape_literal<"?47h">;

static constexpr auto screen_restore_v = detail::escape_literal<"?47l">;

// Show/hide cursor
static constexpr auto cursor_show_v = detail::escape_literal<"?25h">;

static constexpr auto cursor_hide_v = detail::escape_literal<"?25l">;

// Save/restore cursor
static constexpr auto cursor_save_v = detail::escape_literal<"s">;
static constexpr auto cursor_restore_v = detail::escape_literal<"u">;

// Enable/disable alternate buffer
static constexpr auto abuf_enable_v = detail::escape_literal<"?1049h">;

static constexpr auto abuf_disable_v = detail::escape_literal<"?1049l">;

// Enable/disable bracketed paste mode
static constexpr auto paste_enable_v = detail::escape_literal<"?2004h">;

static constexpr auto paste_disable_v = detail::escape_literal<"?2004l">;

// Run-time functions

@@ -231,90 +96,90 @@ static constexpr auto paste_disable_v = detail::escape_literal<"?2004l">;

// Move cursor up/down/frwd/back
static constexpr auto cursor_up(unsigned cnt)
{
return detail::make(cnt, 'A');
return detail::test('A', cnt);
}

static constexpr auto cursor_down(unsigned cnt)
{
return detail::make(cnt, 'B');
return detail::test('B', cnt);
}

static constexpr auto cursor_frwd(unsigned cnt)
{
return detail::make(cnt, 'C');
return detail::test('C', cnt);
}

static constexpr auto cursor_back(unsigned cnt)
{
return detail::make(cnt, 'D');
return detail::test('D', cnt);
}

// Move cursor to the next/prev line
static constexpr auto cursor_line_next(unsigned cnt)
{
return detail::make(cnt, 'E');
return detail::test('E', cnt);
}

static constexpr auto cursor_line_prev(unsigned cnt)
{
return detail::make(cnt, 'F');
return detail::test('F', cnt);
}

// Set cursor to specific column
static constexpr auto cursor_column(unsigned col)
{
return detail::make(col, 'G');
return detail::test('G', col);
}

// Erase functions
static constexpr auto erase_display(motion::enum_type motion)
{
return detail::make(motion(), 'J');
return detail::test('J', motion());
}

static constexpr auto erase_line(motion::enum_type motion)
{
return detail::make(motion(), 'K');
return detail::test('K', motion());
}

// Scroll up/down
static constexpr auto scroll_up(unsigned cnt)
{
return detail::make(cnt, 'S');
return detail::test('S', cnt);
}

static constexpr auto scroll_down(unsigned cnt)
{
return detail::make(cnt, 'T');
return detail::test('T', cnt);
}

// Set cursor to a specific position
static constexpr auto cursor_position(unsigned row, unsigned col)
{
return detail::make(row, ';', col, 'H');
return detail::test('H', row, col);
}

// color
// palet colors
static constexpr auto foreground(color::enum_type color)
{
return detail::make(color() + 30, 'm');
return detail::test('m', color() + 30);
}

static constexpr auto background(color::enum_type color)
{
return detail::make(color() + 40, 'm');
return detail::test('m', color() + 40);
}

// 256-color palette
static constexpr auto foreground(based::bu8 idx)
{
return detail::make(38, ';', 5, ';', idx, 'm');
return detail::test('m', 38, 5, idx);
}

static constexpr auto background(based::bu8 idx)
{
return detail::make(48, ';', 5, ';', idx, 'm');
return detail::test('m', 48, 5, idx);
}

// RGB colors

@@ -322,36 +187,138 @@ static constexpr auto foreground(

based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::make(38, ';', 2, ';', red, ';', green, ';', blue, 'm');
return detail::test('m', 38, 2, red, green, blue);
}

static constexpr auto background(
based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::make(48, ';', 2, ';', red, ';', green, ';', blue, 'm');
return detail::test('m', 48, 2, red, green, blue);
}

// Set/reset text decorators
static constexpr auto decor_set(decor::enum_type decor)
{
return detail::make(decor(), 'm');
return detail::test('m', decor());
}

static constexpr auto decor_reset(decor::enum_type decor)
{
return detail::make(decor() + 20, 'm');
return detail::test('m', decor() + 20);
}

// Set screen modes
static constexpr auto screen_mode_set(unsigned mode)
{
return detail::make('=', mode, 'h');
return detail::test('h', '=', mode);
}

static constexpr auto screen_mode_reset(unsigned mode)
{
return detail::make('=', mode, 'l');
return detail::test('l', '=', mode);
}

// Template specializations

// Move cursor up/down/frwd/back
template<unsigned cnt>
static constexpr auto cursor_up_v = detail::escape<cursor_up, cnt>;

template<unsigned cnt>
static constexpr auto cursor_down_v = detail::escape<cursor_down, cnt>;

template<unsigned cnt>
static constexpr auto cursor_frwd_v = detail::escape<cursor_frwd, cnt>;

template<unsigned cnt>
static constexpr auto cursor_back_v = detail::escape<cursor_back, cnt>;

// Move cursor to the next/prev line
template<unsigned cnt>
static constexpr auto cursor_line_next_v =
detail::escape<cursor_line_next, cnt>;

template<unsigned cnt>
static constexpr auto cursor_line_prev_v =
detail::escape<cursor_line_prev, cnt>;

// Set cursor to specific column
template<unsigned cnt>
static constexpr auto cursor_column_v = detail::escape<cursor_column, cnt>;

// Erase functions
template<motion::enum_type motion>
static constexpr auto erase_display_v = detail::escape<erase_display, motion>;

template<motion::enum_type motion>
static constexpr auto erase_line_v = detail::escape<erase_line, motion>;

// Scroll up/down
template<unsigned cnt>
static constexpr auto scroll_up_v = detail::escape<scroll_up, cnt>;

template<unsigned cnt>
static constexpr auto scroll_down_v = detail::escape<scroll_down, cnt>;

// Set cursor to a specific position
template<unsigned row, unsigned col>
static constexpr auto cursor_position_v =
detail::escape<cursor_position, row, col>;

// Color

template<auto... val>
static constexpr std::string_view foreground_v;

template<auto... val>
static constexpr std::string_view background_v;

template<color::enum_type color>
static constexpr auto foreground_v<color> = detail::
escape<static_cast<std::string (*)(color::enum_type)>(foreground), color>;

template<color::enum_type color>
static constexpr auto background_v<color> = detail::
escape<static_cast<std::string (*)(color::enum_type)>(background), color>;

template<based::bu8 idx>
static constexpr auto foreground_v<idx> =
detail::escape<static_cast<std::string (*)(based::bu8)>(foreground), idx>;

template<based::bu8 idx>
static constexpr auto background_v<idx> =
detail::escape<static_cast<std::string (*)(based::bu8)>(background), idx>;

template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto foreground_v<red, green, blue> = detail::escape<
static_cast<std::string (*)(based::bu8, based::bu8, based::bu8)>(foreground
),
red,
green,
blue>;

template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto background_v<red, green, blue> = detail::escape<
static_cast<std::string (*)(based::bu8, based::bu8, based::bu8)>(background
),
red,
green,
blue>;

// Set/reset text decorators
template<decor::enum_type decor>
static constexpr auto decor_set_v = detail::escape<decor_set, decor>;

template<decor::enum_type decor>
static constexpr auto decor_reset_v = detail::escape<decor_reset, decor>;

// Set screen modes
template<unsigned mode>
static constexpr auto screen_mode_set_v = detail::escape<screen_mode_set, mode>;

template<unsigned mode>
static constexpr auto screen_mode_reset_v =
detail::escape<screen_mode_reset, mode>;

} // namespace alec