Packages

A Gleam library for styling string in the terminal with ANSI Escape Sequences

Current section

Files

Jump to
galant src galant.gleam
Raw

src/galant.gleam

import gleam/bit_string.{to_string}
import gleam/result
import gleam/int
import gleam/list.{fold, map, reverse, sized_chunk}
import gleam/string_builder.{StringBuilder, append, new}
import gleam/option.{None, Option, Some, unwrap}
const black_code = "[30m"
const black_bg_code = "[40m"
const red_code = "[31m"
const red_bg_code = "[41m"
const green_code = "[32m"
const green_bg_code = "[42m"
const yellow_code = "[33m"
const yellow_bg_code = "[43m"
const blue_code = "[34m"
const blue_bg_code = "[44m"
const magenta_code = "[35m"
const magenta_bg_code = "[45m"
const cyan_code = "[36m"
const cyan_bg_code = "[46m"
const white_code = "[37m"
const white_bg_code = "[47m"
const default_code = "[39m"
const default_bg_code = "[49m"
const reset_code = "[0m"
const start_bold_code = "[1m"
const end_bold_code = "[22m"
const start_dim_code = "[2m"
const end_dim_code = "[22m"
const start_italic_code = "[3m"
const end_italic_code = "[23m"
const start_underline_code = "[4m"
const end_underline_code = "[24m"
const start_blinking_code = "[5m"
const end_blinking_code = "[25m"
const start_reverse_code = "[7m"
const end_reverse_code = "[27m"
const start_hidden_code = "[8m"
const end_hidden_code = "[28m"
const start_strikethrough_code = "[9m"
const end_strikethrough_code = "[29m"
const rgb_code = "[38;2;"
const rgb_bg_code = "[48;2;"
/// Styling used to build the StylingSequence
pub opaque type Styling {
Text(text: String)
Black(text: String)
BlackBg(text: String)
StartBlack
StartBlackBg
Red(text: String)
RedBg(text: String)
StartRed
StartRedBg
Green(text: String)
GreenBg(text: String)
StartGreen
StartGreenBg
Yellow(text: String)
YellowBg(text: String)
StartYellow
StartYellowBg
Blue(text: String)
BlueBg(text: String)
StartBlue
StartBlueBg
Magenta(text: String)
MagentaBg(text: String)
StartMagenta
StartMagentaBg
Cyan(text: String)
CyanBg(text: String)
StartCyan
StartCyanBg
White(text: String)
WhiteBg(text: String)
StartWhite
StartWhiteBg
StopColor
StopBgColor
Default(text: String)
DefaultBg(text: String)
Color256(text: String, color: Int)
ColorRGB(text: String, red: Int, green: Int, blue: Int)
ColorBgRGB(text: String, red: Int, green: Int, blue: Int)
StartColorRGB(red: Int, green: Int, blue: Int)
StartColorBgRGB(red: Int, green: Int, blue: Int)
Bold(text: String)
StartBold
EndBold
Dim(text: String)
StartDim
EndDim
Italic(text: String)
StartItalic
EndItalic
Underline(text: String)
StartUnderline
EndUnderline
Blinking(text: String)
StartBlinking
EndBlinking
Reverse(text: String)
StartReverse
EndReverse
Hidden(text: String)
StartHidden
EndHidden
Strikethrough(text: String)
StartStrikethrough
EndStrikethrough
Reset
PlaceHolder
}
type StylingSequence =
List(Styling)
fn color256_code(code: Int) {
"[38;5;" <> int.to_string(code) <> "m"
}
fn color_rgb_code(code: String, red: Int, green: Int, blue: Int) {
new()
|> append(code)
|> append(int.to_string(red))
|> append(";")
|> append(int.to_string(green))
|> append(";")
|> append(int.to_string(blue))
|> append("m")
|> string_builder.to_string()
}
fn esc() -> String {
assert Ok(escape) =
<<0x1b>>
|> bit_string.to_string()
escape
}
fn code(style_code: String) {
esc() <> style_code
}
fn style_text(
builder: StringBuilder,
text: String,
style: String,
reset_style: Option(String),
) -> StringBuilder {
builder
|> append(code(style))
|> append(text)
|> append(unwrap(option.map(reset_style, code), ""))
}
fn hex_to_rgb(color: Int) {
assert Ok([Ok(red), Ok(green), Ok(blue)]) =
int.digits(color, 16)
|> result.map(sized_chunk(_, into: 2))
|> result.map(map(_, int.undigits(_, 16)))
#(red, green, blue)
}
/// Start a new styled sequence
pub fn open() -> StylingSequence {
[]
}
// Style the text in black
pub fn black(styled_text: StylingSequence, text: String) -> StylingSequence {
[Black(text), ..styled_text]
}
// Style the background of the text in black
pub fn bg_black(styled_text: StylingSequence, text: String) -> StylingSequence {
[BlackBg(text), ..styled_text]
}
// Use black for subsequent pars of the styling sequence
pub fn start_black(styled_text: StylingSequence) -> StylingSequence {
[StartBlack, ..styled_text]
}
pub fn start_bg_black(styled_text: StylingSequence) -> StylingSequence {
[StartBlackBg, ..styled_text]
}
pub fn red(styled_text: StylingSequence, text: String) -> StylingSequence {
[Red(text), ..styled_text]
}
pub fn bg_red(styled_text: StylingSequence, text: String) -> StylingSequence {
[RedBg(text), ..styled_text]
}
pub fn start_red(styled_text: StylingSequence) -> StylingSequence {
[StartRed, ..styled_text]
}
pub fn start_bg_red(styled_text: StylingSequence) -> StylingSequence {
[StartRedBg, ..styled_text]
}
pub fn yellow(styled_text: StylingSequence, text: String) -> StylingSequence {
[Yellow(text), ..styled_text]
}
pub fn bg_yellow(styled_text: StylingSequence, text: String) -> StylingSequence {
[YellowBg(text), ..styled_text]
}
pub fn start_yellow(styled_text: StylingSequence) -> StylingSequence {
[StartYellow, ..styled_text]
}
pub fn start_bg_yellow(styled_text: StylingSequence) -> StylingSequence {
[StartYellowBg, ..styled_text]
}
pub fn magenta(styled_text: StylingSequence, text: String) -> StylingSequence {
[Magenta(text), ..styled_text]
}
pub fn bg_magenta(styled_text: StylingSequence, text: String) -> StylingSequence {
[MagentaBg(text), ..styled_text]
}
pub fn start_magenta(styled_text: StylingSequence) -> StylingSequence {
[StartMagenta, ..styled_text]
}
pub fn start_bg_magenta(styled_text: StylingSequence) -> StylingSequence {
[StartMagentaBg, ..styled_text]
}
pub fn cyan(styled_text: StylingSequence, text: String) -> StylingSequence {
[Cyan(text), ..styled_text]
}
pub fn bg_cyan(styled_text: StylingSequence, text: String) -> StylingSequence {
[CyanBg(text), ..styled_text]
}
pub fn start_cyan(styled_text: StylingSequence) -> StylingSequence {
[StartCyan, ..styled_text]
}
pub fn start_bg_cyan(styled_text: StylingSequence) -> StylingSequence {
[StartCyanBg, ..styled_text]
}
pub fn white(styled_text: StylingSequence, text: String) -> StylingSequence {
[White(text), ..styled_text]
}
pub fn bg_white(styled_text: StylingSequence, text: String) -> StylingSequence {
[WhiteBg(text), ..styled_text]
}
pub fn start_white(styled_text: StylingSequence) -> StylingSequence {
[StartWhite, ..styled_text]
}
pub fn start_bg_white(styled_text: StylingSequence) -> StylingSequence {
[StartWhiteBg, ..styled_text]
}
pub fn default(styled_text: StylingSequence, text: String) -> StylingSequence {
[Default(text), ..styled_text]
}
pub fn bg_default(styled_text: StylingSequence, text: String) -> StylingSequence {
[DefaultBg(text), ..styled_text]
}
pub fn blue(styled_text: StylingSequence, text: String) -> StylingSequence {
[Blue(text), ..styled_text]
}
pub fn bg_blue(styled_text: StylingSequence, text: String) -> StylingSequence {
[BlueBg(text), ..styled_text]
}
pub fn start_blue(styled_text: StylingSequence) -> StylingSequence {
[StartBlue, ..styled_text]
}
pub fn start_bg_blue(styled_text: StylingSequence) -> StylingSequence {
[StartBlueBg, ..styled_text]
}
pub fn green(styled_text: StylingSequence, text: String) -> StylingSequence {
[Green(text), ..styled_text]
}
pub fn bg_green(styled_text: StylingSequence, text: String) -> StylingSequence {
[GreenBg(text), ..styled_text]
}
pub fn start_green(styled_text: StylingSequence) -> StylingSequence {
[StartGreen, ..styled_text]
}
pub fn start_bg_green(styled_text: StylingSequence) -> StylingSequence {
[StartGreenBg, ..styled_text]
}
pub fn stop_color(styled_text: StylingSequence) -> StylingSequence {
[StopColor, ..styled_text]
}
pub fn stop_bg_color(styled_text: StylingSequence) -> StylingSequence {
[StopBgColor, ..styled_text]
}
pub fn color_256(
styled_text: StylingSequence,
text: String,
color: Int,
) -> StylingSequence {
[Color256(text, color), ..styled_text]
}
pub fn color_rgb(
styled_text: StylingSequence,
text: String,
red: Int,
green: Int,
blue: Int,
) -> StylingSequence {
[ColorRGB(text, red, green, blue), ..styled_text]
}
pub fn color_bg_rgb(
styled_text: StylingSequence,
text: String,
red: Int,
green: Int,
blue: Int,
) -> StylingSequence {
[ColorBgRGB(text, red, green, blue), ..styled_text]
}
pub fn start_color_rgb(
styled_text: StylingSequence,
red: Int,
green: Int,
blue: Int,
) -> StylingSequence {
[StartColorRGB(red, green, blue), ..styled_text]
}
pub fn start_bg_color_rgb(
styled_text: StylingSequence,
red: Int,
green: Int,
blue: Int,
) -> StylingSequence {
[StartColorBgRGB(red, green, blue), ..styled_text]
}
pub fn color_hex(
styled_text: StylingSequence,
text: String,
color: Int,
) -> StylingSequence {
let #(red, green, blue) = hex_to_rgb(color)
[ColorRGB(text, red, green, blue), ..styled_text]
}
pub fn color_bg_hex(
styled_text: StylingSequence,
text: String,
color: Int,
) -> StylingSequence {
let #(red, green, blue) = hex_to_rgb(color)
[ColorBgRGB(text, red, green, blue), ..styled_text]
}
pub fn start_color_hex(
styled_text: StylingSequence,
color: Int,
) -> StylingSequence {
let #(red, green, blue) = hex_to_rgb(color)
[StartColorRGB(red, green, blue), ..styled_text]
}
pub fn start_bg_color_hex(
styled_text: StylingSequence,
color: Int,
) -> StylingSequence {
let #(red, green, blue) = hex_to_rgb(color)
[StartColorBgRGB(red, green, blue), ..styled_text]
}
pub fn text(styled_text: StylingSequence, text: String) -> StylingSequence {
[Text(text), ..styled_text]
}
pub fn placeholder(styled_text: StylingSequence) -> StylingSequence {
[PlaceHolder, ..styled_text]
}
pub fn bold(styled_text: StylingSequence, text: String) -> StylingSequence {
[Bold(text), ..styled_text]
}
pub fn start_bold(styled_text: StylingSequence) -> StylingSequence {
[StartBold, ..styled_text]
}
pub fn end_bold(styled_text: StylingSequence) -> StylingSequence {
[EndBold, ..styled_text]
}
pub fn dim(styled_text: StylingSequence, text: String) -> StylingSequence {
[Dim(text), ..styled_text]
}
pub fn start_dim(styled_text: StylingSequence) -> StylingSequence {
[StartDim, ..styled_text]
}
pub fn end_dim(styled_text: StylingSequence) -> StylingSequence {
[EndDim, ..styled_text]
}
pub fn italic(styled_text: StylingSequence, text: String) -> StylingSequence {
[Italic(text), ..styled_text]
}
pub fn start_italic(styled_text: StylingSequence) -> StylingSequence {
[StartItalic, ..styled_text]
}
pub fn end_italic(styled_text: StylingSequence) -> StylingSequence {
[EndItalic, ..styled_text]
}
pub fn underline(styled_text: StylingSequence, text: String) -> StylingSequence {
[Underline(text), ..styled_text]
}
pub fn start_underline(styled_text: StylingSequence) -> StylingSequence {
[StartUnderline, ..styled_text]
}
pub fn end_underline(styled_text: StylingSequence) -> StylingSequence {
[EndUnderline, ..styled_text]
}
pub fn blinking(styled_text: StylingSequence, text: String) -> StylingSequence {
[Blinking(text), ..styled_text]
}
pub fn start_blinking(styled_text: StylingSequence) -> StylingSequence {
[StartBlinking, ..styled_text]
}
pub fn end_blinking(styled_text: StylingSequence) -> StylingSequence {
[EndBlinking, ..styled_text]
}
pub fn inverse(styled_text: StylingSequence, text: String) -> StylingSequence {
[Reverse(text), ..styled_text]
}
pub fn start_inverse(styled_text: StylingSequence) -> StylingSequence {
[StartReverse, ..styled_text]
}
pub fn end_inverse(styled_text: StylingSequence) -> StylingSequence {
[EndReverse, ..styled_text]
}
pub fn hidden(styled_text: StylingSequence, text: String) -> StylingSequence {
[Hidden(text), ..styled_text]
}
pub fn start_hidden(styled_text: StylingSequence) -> StylingSequence {
[StartHidden, ..styled_text]
}
pub fn end_hidden(styled_text: StylingSequence) -> StylingSequence {
[EndHidden, ..styled_text]
}
pub fn strikethrough(
styled_text: StylingSequence,
text: String,
) -> StylingSequence {
[Strikethrough(text), ..styled_text]
}
pub fn start_strikethrough(styled_text: StylingSequence) -> StylingSequence {
[StartStrikethrough, ..styled_text]
}
pub fn end_strikethrough(styled_text: StylingSequence) -> StylingSequence {
[EndStrikethrough, ..styled_text]
}
pub fn reset(styled_text: StylingSequence) -> StylingSequence {
[Reset, ..styled_text]
}
pub fn to_string(styled_text: StylingSequence) -> String {
let parts =
styled_text
|> reset()
|> reverse()
parts
|> fold(
new(),
fn(builder, part) {
case part {
Black(text) ->
builder
|> style_text(text, black_code, Some(default_code))
BlackBg(text) ->
builder
|> style_text(text, black_bg_code, None)
StartBlack ->
builder
|> append(code(black_code))
StartBlackBg ->
builder
|> append(code(black_bg_code))
StartRed ->
builder
|> append(code(red_code))
StartRedBg ->
builder
|> append(code(red_bg_code))
Red(text) ->
builder
|> style_text(text, red_code, Some(default_code))
RedBg(text) ->
builder
|> style_text(text, red_bg_code, None)
Yellow(text) ->
builder
|> style_text(text, yellow_code, Some(default_code))
YellowBg(text) ->
builder
|> style_text(text, yellow_bg_code, None)
StartYellow ->
builder
|> append(code(yellow_code))
StartYellowBg ->
builder
|> append(code(yellow_bg_code))
Magenta(text) ->
builder
|> style_text(text, magenta_code, Some(default_code))
MagentaBg(text) ->
builder
|> style_text(text, magenta_bg_code, None)
StartMagenta ->
builder
|> append(code(magenta_code))
StartMagentaBg ->
builder
|> append(code(magenta_bg_code))
Cyan(text) ->
builder
|> style_text(text, cyan_code, Some(default_code))
CyanBg(text) ->
builder
|> style_text(text, cyan_bg_code, None)
StartCyan ->
builder
|> append(code(cyan_code))
StartCyanBg ->
builder
|> append(code(cyan_bg_code))
White(text) ->
builder
|> style_text(text, white_code, Some(default_code))
WhiteBg(text) ->
builder
|> style_text(text, white_bg_code, None)
StartWhite ->
builder
|> append(code(white_code))
StartWhiteBg ->
builder
|> append(code(white_bg_code))
Blue(text) ->
builder
|> style_text(text, blue_code, Some(default_code))
BlueBg(text) ->
builder
|> style_text(text, blue_bg_code, None)
StartBlue ->
builder
|> append(code(blue_code))
StartBlueBg ->
builder
|> append(code(blue_bg_code))
Green(text) ->
builder
|> style_text(text, green_code, Some(default_code))
GreenBg(text) ->
builder
|> style_text(text, green_bg_code, None)
StartGreen ->
builder
|> append(code(green_code))
StartGreenBg ->
builder
|> append(code(green_bg_code))
StopColor ->
builder
|> append(code(default_code))
StopBgColor ->
builder
|> append(code(default_bg_code))
Default(text) ->
builder
|> style_text(text, default_code, None)
DefaultBg(text) ->
builder
|> style_text(text, default_bg_code, None)
Color256(text, color) ->
builder
|> append(code(color256_code(color)))
|> append(text)
ColorRGB(text, red, green, blue) ->
builder
|> append(code(color_rgb_code(rgb_code, red, green, blue)))
|> append(text)
ColorBgRGB(text, red, green, blue) ->
builder
|> append(code(color_rgb_code(rgb_bg_code, red, green, blue)))
|> append(text)
StartColorRGB(red, green, blue) ->
builder
|> append(code(color_rgb_code(rgb_code, red, green, blue)))
StartColorBgRGB(red, green, blue) ->
builder
|> append(code(color_rgb_code(rgb_bg_code, red, green, blue)))
Bold(text) ->
builder
|> style_text(text, start_bold_code, Some(end_bold_code))
StartBold ->
builder
|> append(code(start_bold_code))
EndBold ->
builder
|> append(code(end_bold_code))
Dim(text) ->
builder
|> style_text(text, start_dim_code, Some(end_dim_code))
StartDim ->
builder
|> append(code(start_dim_code))
EndDim ->
builder
|> append(code(end_dim_code))
Italic(text) ->
builder
|> style_text(text, start_italic_code, Some(end_italic_code))
StartItalic ->
builder
|> append(code(start_italic_code))
EndItalic ->
builder
|> append(code(end_italic_code))
Underline(text) ->
builder
|> style_text(text, start_underline_code, Some(end_underline_code))
StartUnderline ->
builder
|> append(code(start_underline_code))
EndUnderline ->
builder
|> append(code(end_underline_code))
Blinking(text) ->
builder
|> style_text(text, start_blinking_code, Some(end_blinking_code))
StartBlinking ->
builder
|> append(code(start_blinking_code))
EndBlinking ->
builder
|> append(code(end_blinking_code))
Reverse(text) ->
builder
|> style_text(text, start_reverse_code, Some(end_reverse_code))
StartReverse ->
builder
|> append(code(start_reverse_code))
EndReverse ->
builder
|> append(code(end_reverse_code))
Hidden(text) ->
builder
|> style_text(text, start_hidden_code, Some(end_hidden_code))
StartHidden ->
builder
|> append(code(start_hidden_code))
EndHidden ->
builder
|> append(code(end_hidden_code))
Strikethrough(text) ->
builder
|> style_text(
text,
start_strikethrough_code,
Some(end_strikethrough_code),
)
StartStrikethrough ->
builder
|> append(code(start_strikethrough_code))
EndStrikethrough ->
builder
|> append(code(end_strikethrough_code))
Reset ->
builder
|> append(code(reset_code))
Text(text) ->
builder
|> append(text)
PlaceHolder -> builder
}
},
)
|> string_builder.to_string()
}
pub fn save(
this_styled_text: StylingSequence,
) -> fn(StylingSequence, String) -> StylingSequence {
fn(styled_text: StylingSequence, text: String) {
this_styled_text
|> map(fn(part) {
case part {
PlaceHolder -> Text(text)
other -> other
}
})
|> list.append(styled_text)
}
}
pub fn to_string_styler(
this_styled_text: StylingSequence,
) -> fn(String) -> String {
fn(text: String) {
this_styled_text
|> map(fn(part) {
case part {
PlaceHolder -> Text(text)
other -> other
}
})
|> to_string()
}
}