///|
/// ANSI escape-sequence helpers for terminal rendering.
///
/// These helpers produce raw ANSI escape codes that can be embedded in the
/// string returned by a `view` function. The Pippa runtime writes the
/// resulting string directly to the terminal.
///|
/// Escape character.
pub const ESC : String = "\u001b"
///|
/// CSI prefix (`ESC [`).
pub const CSI : String = "\u001b["
///|
/// OSC prefix (`ESC ]`).
pub const OSC : String = "\u001b]"
///|
/// DCS prefix (`ESC P`).
pub const DCS : String = "\u001bP"
///|
/// String terminator (`ESC \`).
pub const ST : String = "\u001b\\"
///|
/// Reset all attributes (CSI 0 m).
pub fn reset() -> String {
"\{CSI}0m"
}
///|
/// Bold text (CSI 1 m).
pub fn bold() -> String {
"\{CSI}1m"
}
///|
/// Dim / faint text (CSI 2 m).
pub fn dim() -> String {
"\{CSI}2m"
}
///|
/// Underlined text (CSI 4 m).
pub fn underline() -> String {
"\{CSI}4m"
}
///|
/// Blinking text (CSI 5 m).
pub fn blink() -> String {
"\{CSI}5m"
}
///|
/// Reverse video (CSI 7 m).
pub fn reverse() -> String {
"\{CSI}7m"
}
///|
/// Strikethrough text (CSI 9 m).
pub fn strikethrough() -> String {
"\{CSI}9m"
}
///|
/// Hide the cursor (CSI ? 25 l).
pub fn hide_cursor() -> String {
"\{CSI}?25l"
}
///|
/// Show the cursor (CSI ? 25 h).
pub fn show_cursor() -> String {
"\{CSI}?25h"
}
///|
/// Real terminal cursor shape/blink styles for DECSCUSR.
pub(all) enum CursorStyle {
Default
BlinkBlock
SteadyBlock
BlinkUnderline
SteadyUnderline
BlinkBar
SteadyBar
} derive(Debug, Eq)
///|
fn cursor_style_code(style : CursorStyle) -> String {
match style {
Default => "0"
BlinkBlock => "1"
SteadyBlock => "2"
BlinkUnderline => "3"
SteadyUnderline => "4"
BlinkBar => "5"
SteadyBar => "6"
}
}
///|
/// Set the real terminal cursor shape/blink using DECSCUSR (CSI Ps SP q).
pub fn set_cursor_style(style : CursorStyle) -> String {
"\{CSI}\{cursor_style_code(style)} q"
}
///|
/// Begin synchronized output mode (CSI ? 2026 h).
pub fn begin_sync_update() -> String {
"\{CSI}?2026h"
}
///|
/// End synchronized output mode (CSI ? 2026 l).
pub fn end_sync_update() -> String {
"\{CSI}?2026l"
}
///|
/// Enter the alternate screen buffer (CSI ? 1049 h).
pub fn enter_alt_screen() -> String {
"\{CSI}?1049h"
}
///|
/// Enable normal mouse reporting with SGR coordinates (CSI ?1000h CSI ?1006h).
pub fn enable_mouse() -> String {
"\{CSI}?1000h\{CSI}?1006h"
}
///|
/// Enable mouse button-event reporting with SGR coordinates (CSI ?1002h CSI ?1006h).
pub fn enable_mouse_cell_motion() -> String {
"\{CSI}?1002h\{CSI}?1006h"
}
///|
/// Enable mouse motion reporting with SGR coordinates (CSI ?1003h CSI ?1006h).
pub fn enable_mouse_motion() -> String {
"\{CSI}?1003h\{CSI}?1006h"
}
///|
/// Enable focus reporting (CSI ?1004h).
pub fn enable_focus_reporting() -> String {
"\{CSI}?1004h"
}
///|
/// Enable bracketed paste mode (CSI ?2004h).
pub fn enable_bracketed_paste() -> String {
"\{CSI}?2004h"
}
///|
/// Exit the alternate screen buffer and restore the primary screen (CSI ? 1049 l).
pub fn exit_alt_screen() -> String {
"\{CSI}?1049l"
}
///|
/// Disable focus reporting (CSI ?1004l).
pub fn disable_focus_reporting() -> String {
"\{CSI}?1004l"
}
///|
/// Disable bracketed paste mode (CSI ?2004l).
pub fn disable_bracketed_paste() -> String {
"\{CSI}?2004l"
}
///|
/// Request primary device attributes (DA1).
pub fn request_device_attributes() -> String {
"\{CSI}c"
}
///|
/// Request secondary device attributes / terminal version (DA2).
pub fn request_secondary_device_attributes() -> String {
"\{CSI}>c"
}
///|
/// Request the current cursor position (DSR CPR).
pub fn request_cursor_position() -> String {
"\{CSI}6n"
}
///|
/// Request the current default foreground color (OSC 10).
pub fn request_foreground_color() -> String {
"\{OSC}10;?\{ST}"
}
///|
/// Request the current default background color (OSC 11).
pub fn request_background_color() -> String {
"\{OSC}11;?\{ST}"
}
///|
fn hex_digit(n : Int) -> Char {
if n < 10 {
(0x30 + n).unsafe_to_char()
} else {
(0x61 + n - 10).unsafe_to_char()
}
}
///|
fn hex_encode(input : String) -> String {
let bytes = @utf8.encode(input)
let buf = StringBuilder::new(size_hint=bytes.length() * 2)
let mut i = 0
while i < bytes.length() {
let b = bytes[i].to_int()
buf.write_char(hex_digit((b >> 4) & 0x0F))
buf.write_char(hex_digit(b & 0x0F))
i = i + 1
}
buf.to_string()
}
///|
/// Request an XTGETTCAP capability by name.
pub fn request_capability(name : String) -> String {
"\{DCS}+q\{hex_encode(name)}\{ST}"
}
///|
/// Request clipboard contents from an OSC 52 selection.
pub fn request_clipboard(selection : ClipboardSelection) -> String {
"\{OSC}52;\{clipboard_selection_code(selection)};?\{ST}"
}
///|
/// Set clipboard contents for an OSC 52 selection.
pub fn set_clipboard(selection : ClipboardSelection, text : String) -> String {
"\{OSC}52;\{clipboard_selection_code(selection)};\{encode_base64_utf8(text)}\{ST}"
}
///|
fn osc8(params : String, uri : String, label : String) -> String {
"\{OSC}8;\{params};\{uri}\{ST}\{label}\{OSC}8;;\{ST}"
}
///|
/// Wrap a label in an OSC 8 hyperlink with empty parameters.
pub fn hyperlink(url : String, label : String) -> String {
osc8("", url, label)
}
///|
/// Disable all mouse reporting (CSI ?1000l, CSI ?1002l, CSI ?1003l, and CSI ?1006l).
pub fn disable_mouse() -> String {
"\{CSI}?1000l\{CSI}?1002l\{CSI}?1003l\{CSI}?1006l"
}
///|
/// Clear the entire screen (CSI 2 J).
pub fn clear_screen() -> String {
"\{CSI}2J"
}
///|
/// Clear from cursor to end of line (CSI K).
pub fn clear_line() -> String {
"\{CSI}K"
}
///|
/// Clear from cursor to end of screen (CSI J).
pub fn clear_screen_below() -> String {
"\{CSI}J"
}
///|
/// Move cursor to (row, col), 1-based (CSI row ; col H).
pub fn move_cursor(row : Int, col : Int) -> String {
"\{CSI}\{row};\{col}H"
}
///|
/// Set the foreground color (256-color mode: CSI 38 ; 5 ; n m).
pub fn fg_color(n : Int) -> String {
"\{CSI}38;5;\{n}m"
}
///|
/// Set the background color (256-color mode: CSI 48 ; 5 ; n m).
pub fn bg_color(n : Int) -> String {
"\{CSI}48;5;\{n}m"
}
///|
/// Set the foreground to an RGB color (CSI 38 ; 2 ; r ; g ; b m).
pub fn fg_rgb(r : Int, g : Int, b : Int) -> String {
"\{CSI}38;2;\{r};\{g};\{b}m"
}
///|
/// Set the background to an RGB color (CSI 48 ; 2 ; r ; g ; b m).
pub fn bg_rgb(r : Int, g : Int, b : Int) -> String {
"\{CSI}48;2;\{r};\{g};\{b}m"
}
///|
/// Reset the foreground color to the terminal default (CSI 39 m).
pub fn reset_foreground_color() -> String {
"\{CSI}39m"
}
///|
/// Reset the background color to the terminal default (CSI 49 m).
pub fn reset_background_color() -> String {
"\{CSI}49m"
}
///|
/// Set the terminal window title using OSC 2.
pub fn set_window_title(title : String) -> String {
"\{OSC}2;\{title}\{ST}"
}
///|
/// Clear the terminal window title using OSC 2 with an empty title.
pub fn clear_window_title() -> String {
set_window_title("")
}
///|
fn clamp_progress_value(value : Int) -> Int {
value.max(0).min(100)
}
///|
/// Set terminal progress using the ConEmu / Windows Terminal OSC 9;4 protocol.
pub fn set_terminal_progress(state : Int, value : Int) -> String {
"\{OSC}9;4;\{state};\{clamp_progress_value(value)}\{ST}"
}
///|
/// Clear terminal progress using OSC 9;4 state 0.
pub fn clear_terminal_progress() -> String {
set_terminal_progress(0, 0)
}