-module(string_width).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/string_width.gleam").
-export([new/0, mode_2027/1, mode_2027_ext/1, mode_wcwidth/1, ambiguous_as_wide/1, count_ansi_codes/1, with_tab_width/2, at_tab_offset/2, get_terminal_size/0, is_tty/0, dimensions_with/2, line/1, line_with/2, dimensions/1, is_ansi_component/2, fold_raw/4, fold_with/4, soft_wrap_with/5, limit_with/4, limit/3, soft_wrap/4, fold_raw_pieces/4, tabs_to_spaces_with/2, tabs_to_spaces/1, position_with/6, position/5, align_with/5, align/4, padding/6, padding_with/7, scroll/5, scroll_with/6, stack_vertical_with/5, stack_vertical/4, stack_horizontal_with/5, stack_horizontal/4, inline_styles/1, strip_ansi/1, fold/3]).
-export_type([options/0, mode/0, size/0, indent/0, limit_state/0, alignment/0, placement/0, view_state/1, piece/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" \n"
" \n"
"\n"
"\n"
" The `with` variants of all functions additionally work with custom options.\n"
"\n"
" **Tip:** Hover the links for short summaries!\n"
"\n"
" #### Measure\n"
" [line](#line \"Width of a line\")[[_with](#line_with)]\n"
" [dimensions](#dimensions \"Size of a string\")[[_with](#dimensions_with)],\n"
" [get_terminal_size](#get_terminal_size \"Size of the terminal window\"),\n"
" [is_tty](#is_tty \"Check if this is an interactive terminal\")\n"
"\n"
" #### Layout & Positioning\n"
"\n"
" [limit](#limit \"Word wrapping and truncation\")[[_with](#limit_with)],\n"
" [soft_wrap](#soft_wrap \"Word wrapping with custom indentation logic\")[[_with](#soft_wrap_with)],\n"
" [align](#align \"Text alignment\")[[_with](#align_with)],\n"
" [tabs_to_spaces](#tabs_to_spaces \"Pin tab widths\")[[_with](#tabs_to_spaces_with)]\n"
"\n"
" [stack_horizontal](#stack_horizontal \"Column-based layouts\")[[_with](#stack_horizontal_with)],\n"
" [stack_vertical](#stack_vertical \"Row-based layouts\")[[_with](#stack_vertical_with)],\n"
" [padding](#padding \"Add extra space around a string\")[[_with](#padding_with)],\n"
" [position](#position \"Put a string inside a fixed-size box\")[[_with](#position_with)],\n"
" [scroll](#scroll \"Cutout a viewport area from a string\")[[_with](#scroll_with)]\n"
"\n"
" #### ANSI escape sequence helpers\n"
" [inline_styles](#inline_styles \"Ensure styling doesn't overflow lines/columns\"),\n"
" [strip_ansi](#strip_ansi \"Remove all styles\"),\n"
" [is_ansi_component](#is_ansi_component \"Helper for fold\")\n"
"\n"
" #### Options Builder\n"
" [new](#new \"Default options\"),\n"
" [ambiguous_as_wide](#ambiguous_as_wide \"CJK support\"),\n"
" [count_ansi_codes](#count_ansi_codes \"Non-terminal support\"),\n"
" [mode_2027](#mode_2027 \"Compatibility with other libraries\"),\n"
" [mode_2027_ext](#mode_2027_ext \"Compatibility with other libraries with extra salt\"),\n"
" [mode_wcwidth](#mode_wcwidth \"Default mode, use this for terminals!\"),\n"
" [at_tab_offset](#at_tab_offset \"Control tab width calculations\"),\n"
" [with_tab_width](#with_tab_width \"Control tab width calculations\")\n"
"\n"
" #### Advanced\n"
" [fold](#fold \"Loop grapheme clusters and their position/size\")[[_with](#fold_with)],\n"
" [fold_raw](#fold_raw \"Loop low-level segments and their un-processed width\"),\n"
" [fold_raw_pieces](#fold_raw_pieces \"Loop low-level segments and their position/size\")\n"
"\n"
).
-opaque options() :: {options,
boolean(),
boolean(),
mode(),
integer(),
integer()}.
-type mode() :: mode_wcwidth | mode2027 | mode2027_ext.
-type size() :: {size, integer(), integer()}.
-type indent() :: indent_unknown | {indent, binary(), integer()}.
-type limit_state() :: {limit_state,
binary(),
binary(),
integer(),
integer(),
binary(),
integer(),
binary(),
integer(),
binary(),
integer(),
indent(),
boolean()}.
-type alignment() :: left | center | right.
-type placement() :: top | middle | bottom.
-type view_state(DLQ) :: {view_state, DLQ, binary(), integer(), integer()}.
-type piece() :: {piece, binary(), integer(), integer(), integer()}.
-file("src/string_width.gleam", 183).
?DOC(" Start building up new options.\n").
-spec new() -> options().
new() ->
{options, false, false, mode_wcwidth, 8, 0}.
-file("src/string_width.gleam", 204).
?DOC(
" Measure grapheme clusters instead of individual code points.\n"
"\n"
" Most terminal emulators do not handle grapheme clusters well and will\n"
" instead show their decomposition. To make sure a given string always fits\n"
" even on those terminals, the functions in this package will copy this\n"
" behaviour as well.\n"
"\n"
" If you enable this option, the returned numbers will more accurately represent\n"
" the width of a string on a website or in an editor.\n"
"\n"
" **NOTE:** Grapheme support in terminals is highly experimental and subject\n"
" to ongoing discussion! When working on CLIs or TUIs, you do not want to use\n"
" this option most of the time.\n"
"\n"
" See also [Grapheme Clusters and Terminal Emulators](https://mitchellh.com/writing/grapheme-clusters-in-terminals)\n"
" for a better explanation on how terminals behave.\n"
).
-spec mode_2027(options()) -> options().
mode_2027(Options) ->
{options,
erlang:element(2, Options),
erlang:element(3, Options),
mode2027,
erlang:element(5, Options),
erlang:element(6, Options)}.
-file("src/string_width.gleam", 219).
?DOC(
" Measure grapheme clusters instead of individual code points, and always\n"
" treat grapheme clusters with more than one non-modifier character as wide.\n"
"\n"
" This is a custom extension to Unicode Core. Many single grapheme clusters\n"
" would currently still render as wide glyphs in many contexts, since font\n"
" shaping would not be able to collapse them into a single narrow \"ligature\".\n"
"\n"
" This mode is an additional heuristic that tries to handle more such cases.\n"
"\n"
" See `mode_2027` for more explanation on the implications.\n"
).
-spec mode_2027_ext(options()) -> options().
mode_2027_ext(Options) ->
{options,
erlang:element(2, Options),
erlang:element(3, Options),
mode2027_ext,
erlang:element(5, Options),
erlang:element(6, Options)}.
-file("src/string_width.gleam", 231).
?DOC(
" The default mode and the mode suitable for most terminals.\n"
"\n"
" Measures individual code points, similar to the libc `wcwidth` function\n"
" that almost all of these terminals use.\n"
"\n"
" Note that except for `fold_raw`, this library will still always process\n"
" grapheme clusters as a unit.\n"
).
-spec mode_wcwidth(options()) -> options().
mode_wcwidth(Options) ->
{options,
erlang:element(2, Options),
erlang:element(3, Options),
mode_wcwidth,
erlang:element(5, Options),
erlang:element(6, Options)}.
-file("src/string_width.gleam", 242).
?DOC(
" Some characters are marked by Unicode as \"ambiguous\", meaning they may\n"
" occupy 1 or 2 cells, depending on the context, current language, selected\n"
" font, surrounding text, and more.\n"
"\n"
" Unicode recommends treating these characters as narrow by default,\n"
" but you can change this behaviour using this option.\n"
).
-spec ambiguous_as_wide(options()) -> options().
ambiguous_as_wide(Options) ->
{options,
erlang:element(2, Options),
true,
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options)}.
-file("src/string_width.gleam", 251).
?DOC(
" Do not ignore ansi escape sequences, and count them as regular characters.\n"
"\n"
" You can enable this option as an optimisation if you are sure that your\n"
" string doesn't contain any ansi escape codes.\n"
).
-spec count_ansi_codes(options()) -> options().
count_ansi_codes(Options) ->
{options,
true,
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
erlang:element(6, Options)}.
-file("src/string_width.gleam", 260).
?DOC(
" Change the number of columns between tab stops. (Default: 8)\n"
"\n"
" Whenever a tab character `\\t` is encountered, the current column number will\n"
" be rounded up to the next multiple of this number.\n"
).
-spec with_tab_width(options(), integer()) -> options().
with_tab_width(Options, Tab_width) ->
{options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
Tab_width,
erlang:element(6, Options)}.
-file("src/string_width.gleam", 270).
?DOC(
" Define an _offset_ for tab stop calculations. (Default: 0)\n"
"\n"
" If the text you print doesn't start at the first column, but is instead\n"
" indented somehow, you can set this option to this number to make sure tab\n"
" stops are correctly calculated.\n"
).
-spec at_tab_offset(options(), integer()) -> options().
at_tab_offset(Options, Tab_offset) ->
{options,
erlang:element(2, Options),
erlang:element(3, Options),
erlang:element(4, Options),
erlang:element(5, Options),
Tab_offset}.
-file("src/string_width.gleam", 287).
?DOC(
" Get the size of the terminal, if available.\n"
"\n"
" Checks the built-in methods for the target, as well as the `LINES` and\n"
" `COLUMNS` environment variables.\n"
).
-spec get_terminal_size() -> {ok, size()} | {error, nil}.
get_terminal_size() ->
case string_width_ffi:get_terminal_size() of
{ok, {Rows, Columns}} ->
{ok, {size, Rows, Columns}};
_ ->
{error, nil}
end.
-file("src/string_width.gleam", 301).
?DOC(" Checks whether the program is running in an interactive terminal (TTY).\n").
-spec is_tty() -> boolean().
is_tty() ->
string_width_ffi:is_tty().
-file("src/string_width.gleam", 2109).
-spec fold_parts(
binary(),
integer(),
list({integer(), integer()}),
DMW,
fun((DMW, binary()) -> DMW),
fun((DMW, binary(), integer()) -> DMW)
) -> DMW.
fold_parts(String, Offset, Ranges, State, On_chars, On_range) ->
case Ranges of
[{Start, Length} | Ranges@1] when Start =:= Offset ->
{Range_slice, String@1} = erlang:split_binary(String, Length),
State@1 = On_range(State, Range_slice, Length),
Offset@1 = Start + Length,
fold_parts(
String@1,
Offset@1,
Ranges@1,
State@1,
On_chars,
On_range
);
[{Start@1, Length@1} | Ranges@2] ->
{Before, At_range} = erlang:split_binary(String, Start@1 - Offset),
{Range_slice@1, String@2} = erlang:split_binary(At_range, Length@1),
State@2 = begin
_pipe = State,
_pipe@1 = On_chars(_pipe, Before),
On_range(_pipe@1, Range_slice@1, Length@1)
end,
Offset@2 = Start@1 + Length@1,
fold_parts(
String@2,
Offset@2,
Ranges@2,
State@2,
On_chars,
On_range
);
[] ->
case String of
<<""/utf8>> ->
State;
_ ->
On_chars(State, String)
end
end.
-file("src/string_width.gleam", 2309).
-spec table_lookup(bitstring(), integer()) -> boolean().
table_lookup(Table, Value) ->
Hi = erlang:'bsr'(Value, 8),
Md = erlang:'bsr'(erlang:'band'(Value, 16#ff), 3),
Lo = erlang:'band'(Value, 7),
Lvl1_skip = Hi * 8,
Lvl1@1 = case Table of
<<_:Lvl1_skip, Lvl1/integer, _/bitstring>> -> Lvl1;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <>,
module => <<"string_width"/utf8>>,
function => <<"table_lookup"/utf8>>,
line => 2315,
value => _assert_fail,
start => 72379,
'end' => 72437,
pattern_start => 72390,
pattern_end => 72429})
end,
Lvl2_skip = ((Lvl1@1 * 32) + Md) * 8,
Lvl3@1 = case Table of
<<_:Lvl2_skip, Lvl3/integer, _/bitstring>> -> Lvl3;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <>,
module => <<"string_width"/utf8>>,
function => <<"table_lookup"/utf8>>,
line => 2318,
value => _assert_fail@1,
start => 72482,
'end' => 72540,
pattern_start => 72493,
pattern_end => 72532})
end,
(erlang:'bsr'(Lvl3@1, Lo) rem 2) /= 0.
-file("src/string_width.gleam", 2298).
-spec is_spacing_mark(integer()) -> boolean().
is_spacing_mark(Cp) ->
case Cp =< 16#1ffff of
true ->
table_lookup(
<<16,
16,
16,
16,
16,
16,
16,
16,
16,
17,
18,
19,
20,
21,
16,
22,
23,
16,
16,
16,
16,
16,
16,
24,
16,
25,
26,
27,
28,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
29,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
30,
31,
32,
33,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
16,
45,
46,
47,
48,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
49,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
50,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
51,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
8,
0,
0,
0,
0,
0,
0,
200,
1,
222,
0,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
192,
129,
25,
128,
0,
0,
0,
0,
0,
8,
0,
0,
0,
0,
0,
0,
192,
1,
0,
0,
0,
0,
0,
0,
0,
8,
0,
0,
0,
0,
0,
0,
192,
1,
26,
0,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
64,
129,
25,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
198,
29,
128,
0,
0,
0,
0,
0,
14,
0,
0,
0,
0,
0,
0,
0,
30,
0,
0,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
64,
159,
13,
96,
0,
0,
0,
8,
0,
12,
0,
0,
0,
0,
0,
0,
192,
193,
29,
128,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
0,
0,
128,
3,
255,
0,
0,
12,
0,
0,
0,
0,
0,
0,
0,
0,
192,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
2,
25,
0,
0,
192,
0,
156,
63,
0,
0,
152,
159,
0,
28,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
192,
191,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
120,
14,
251,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
6,
0,
0,
0,
0,
0,
0,
160,
0,
26,
224,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
32,
232,
27,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
194,
4,
0,
0,
0,
0,
0,
0,
128,
92,
12,
0,
0,
0,
0,
0,
240,
15,
48,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
128,
0,
0,
0,
0,
0,
0,
192,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
152,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
240,
255,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
0,
8,
0,
0,
0,
0,
0,
48,
204,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
25,
0,
0,
32,
0,
0,
0,
0,
0,
40,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
200,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
216,
22,
0,
0,
5,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
0,
0,
135,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
0,
0,
96,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
0,
0,
56,
128,
1,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
112,
44,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
7,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
192,
158,
57,
128,
0,
12,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
7,
164,
183,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
224,
0,
35,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
7,
122,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
3,
79,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
7,
88,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
208,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
67,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
112,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
191,
33,
5,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
14,
240,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
0,
128,
1,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
18,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
124,
88,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
0,
8,
0,
0,
0,
0,
0,
48,
192,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
28,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
254,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
224,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0>>,
Cp
);
false ->
false
end.
-file("src/string_width.gleam", 2274).
-spec is_ambiguous(integer()) -> boolean().
is_ambiguous(Cp) ->
case Cp =< 16#ffff of
true ->
table_lookup(
<<8,
9,
10,
11,
12,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
14,
15,
16,
17,
18,
19,
20,
21,
13,
13,
13,
22,
13,
13,
13,
13,
13,
13,
23,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
13,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
13,
13,
13,
13,
13,
25,
26,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
146,
101,
223,
247,
64,
0,
129,
193,
67,
55,
141,
87,
2,
0,
10,
8,
192,
8,
14,
129,
23,
47,
12,
0,
192,
8,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
85,
21,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
144,
46,
1,
175,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
254,
255,
251,
3,
254,
255,
251,
3,
0,
0,
0,
0,
0,
0,
2,
0,
255,
255,
255,
255,
255,
255,
255,
255,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
121,
51,
247,
0,
45,
72,
0,
0,
0,
0,
0,
0,
16,
128,
30,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
40,
2,
72,
0,
70,
8,
0,
0,
0,
0,
24,
120,
255,
15,
255,
3,
0,
2,
255,
3,
0,
0,
0,
3,
0,
0,
20,
0,
128,
0,
0,
0,
141,
137,
34,
228,
169,
95,
240,
48,
0,
17,
4,
0,
243,
204,
0,
0,
204,
0,
32,
2,
32,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
251,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
15,
255,
255,
255,
255,
15,
0,
255,
255,
60,
0,
251,
3,
204,
48,
195,
201,
3,
0,
60,
128,
0,
0,
96,
194,
0,
80,
0,
0,
0,
0,
5,
0,
0,
0,
187,
183,
0,
0,
0,
0,
0,
192,
0,
0,
0,
128,
192,
191,
239,
255,
11,
251,
211,
219,
0,
0,
0,
0,
0,
0,
0,
32,
0,
0,
0,
0,
0,
0,
192,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32>>,
Cp
);
false ->
(((((((((Cp >= 16#1f100) andalso (Cp =< 16#1f10a)) orelse ((Cp >= 16#1f110)
andalso (Cp =< 16#1f12d)))
orelse ((Cp >= 16#1f130) andalso (Cp =< 16#1f169)))
orelse ((Cp >= 16#1f170) andalso (Cp =< 16#1f18d)))
orelse ((Cp >= 16#1f18f) andalso (Cp =< 16#1f190)))
orelse ((Cp >= 16#1f19b) andalso (Cp =< 16#1f1ac)))
orelse ((Cp >= 16#e0100) andalso (Cp =< 16#e01ef)))
orelse ((Cp >= 16#f0000) andalso (Cp =< 16#ffffd)))
orelse ((Cp >= 16#100000) andalso (Cp =< 16#10FFFD))
end.
-file("src/string_width.gleam", 2291).
-spec is_wide(integer()) -> boolean().
is_wide(Cp) ->
case Cp =< 16#1ffff of
true ->
table_lookup(
<<16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
17,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
18,
16,
19,
20,
21,
16,
16,
16,
22,
16,
16,
23,
24,
25,
26,
27,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
29,
16,
16,
16,
16,
30,
16,
16,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
31,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
28,
28,
16,
16,
16,
32,
33,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
34,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
35,
28,
28,
28,
28,
36,
37,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
38,
28,
39,
40,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
41,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
42,
43,
44,
45,
46,
47,
48,
49,
16,
50,
51,
16,
16,
16,
16,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
12,
0,
6,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
30,
9,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
0,
0,
48,
0,
0,
0,
255,
0,
0,
255,
15,
0,
0,
0,
0,
128,
0,
252,
8,
0,
2,
12,
0,
96,
48,
64,
16,
0,
0,
4,
44,
36,
32,
12,
0,
0,
0,
1,
0,
0,
0,
80,
184,
0,
0,
0,
0,
0,
0,
0,
224,
0,
0,
0,
1,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
0,
0,
0,
0,
0,
0,
33,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
251,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
15,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
63,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
127,
254,
255,
255,
255,
255,
255,
255,
255,
255,
255,
127,
254,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
224,
255,
255,
255,
255,
255,
254,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
127,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
63,
128,
255,
255,
255,
255,
255,
127,
255,
255,
255,
255,
255,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
31,
255,
255,
255,
255,
255,
255,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
31,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
3,
0,
0,
255,
255,
255,
255,
247,
255,
127,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
254,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
31,
0,
3,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
63,
0,
0,
0,
0,
128,
255,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
239,
111,
255,
255,
255,
255,
7,
0,
4,
0,
0,
0,
39,
0,
240,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
15,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
127,
0,
255,
255,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
254,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
7,
0,
255,
255,
255,
255,
255,
15,
255,
1,
3,
0,
63,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
1,
224,
191,
255,
255,
255,
255,
255,
255,
255,
255,
223,
255,
255,
15,
0,
255,
255,
255,
255,
255,
135,
15,
0,
255,
255,
17,
255,
255,
255,
255,
255,
255,
255,
255,
127,
253,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
159,
255,
255,
255,
255,
255,
255,
255,
63,
0,
120,
255,
255,
255,
0,
0,
4,
0,
0,
96,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
248,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
63,
16,
231,
240,
0,
24,
240,
31,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
15,
1,
0,
0,
240,
255,
255,
255,
255,
255,
247,
191,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
31,
255,
131,
255,
255,
255,
255,
255,
255,
127,
192,
255,
159,
255,
3,
255,
1>>,
Cp
);
false ->
(Cp >= 16#20000) andalso (Cp =< 16#40000)
end.
-file("src/string_width.gleam", 2267).
-spec is_ignorable(integer()) -> boolean().
is_ignorable(Cp) ->
case Cp =< 16#1ffff of
true ->
table_lookup(
<<16,
17,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
17,
33,
17,
17,
17,
34,
35,
36,
37,
38,
39,
40,
17,
17,
41,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
42,
43,
17,
17,
44,
45,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
46,
17,
47,
48,
49,
50,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
51,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
52,
17,
17,
53,
54,
17,
55,
56,
57,
17,
17,
17,
17,
17,
17,
58,
17,
17,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
17,
73,
74,
75,
76,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
77,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
78,
17,
17,
17,
17,
17,
17,
17,
17,
79,
80,
17,
17,
17,
81,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
82,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
83,
17,
84,
85,
17,
17,
17,
17,
17,
17,
17,
86,
17,
17,
17,
17,
17,
87,
80,
88,
17,
89,
90,
17,
17,
91,
92,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
255,
255,
255,
255,
0,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
248,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
254,
255,
255,
255,
255,
191,
182,
0,
0,
0,
0,
0,
0,
0,
63,
0,
255,
23,
0,
0,
0,
0,
0,
248,
255,
255,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
191,
159,
61,
0,
0,
0,
128,
2,
0,
0,
0,
255,
255,
255,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
255,
1,
0,
0,
0,
0,
0,
0,
248,
15,
32,
0,
0,
192,
251,
239,
62,
0,
0,
0,
0,
0,
14,
0,
0,
0,
0,
0,
0,
131,
255,
0,
0,
0,
0,
0,
252,
255,
255,
255,
255,
255,
255,
7,
0,
0,
0,
0,
0,
0,
20,
254,
33,
254,
0,
12,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
16,
30,
32,
0,
0,
12,
0,
0,
64,
6,
0,
0,
0,
0,
0,
0,
16,
134,
57,
2,
0,
0,
0,
35,
0,
6,
0,
0,
0,
0,
0,
0,
16,
190,
33,
0,
0,
12,
0,
0,
252,
2,
0,
0,
0,
0,
0,
0,
144,
30,
32,
96,
0,
12,
0,
0,
0,
4,
0,
0,
0,
0,
0,
0,
0,
1,
32,
0,
0,
0,
0,
0,
0,
17,
0,
0,
0,
0,
0,
0,
208,
193,
61,
96,
0,
12,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
144,
64,
48,
0,
0,
12,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
24,
30,
32,
0,
0,
12,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
0,
0,
4,
92,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
242,
7,
128,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
242,
31,
0,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
160,
2,
0,
0,
0,
0,
0,
0,
254,
127,
223,
224,
255,
254,
255,
255,
255,
31,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
224,
253,
102,
0,
0,
0,
195,
1,
0,
30,
0,
100,
32,
0,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
224,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
28,
0,
0,
0,
12,
0,
0,
0,
12,
0,
0,
0,
12,
0,
0,
0,
0,
0,
0,
0,
176,
63,
64,
254,
15,
32,
0,
0,
0,
0,
0,
248,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
0,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
135,
1,
4,
14,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
9,
0,
0,
0,
0,
0,
0,
64,
127,
229,
31,
248,
159,
0,
0,
0,
0,
0,
0,
255,
255,
255,
127,
0,
0,
0,
0,
0,
0,
15,
0,
0,
0,
0,
0,
208,
23,
4,
0,
0,
0,
0,
248,
15,
0,
3,
0,
0,
0,
60,
59,
0,
0,
0,
0,
0,
0,
64,
163,
3,
0,
0,
0,
0,
0,
0,
240,
207,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
247,
255,
253,
33,
16,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
255,
255,
0,
248,
0,
0,
0,
127,
0,
0,
0,
0,
0,
0,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
0,
0,
0,
0,
0,
60,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
6,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
247,
63,
0,
0,
0,
192,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
68,
8,
0,
0,
96,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
0,
0,
0,
255,
255,
3,
128,
0,
0,
0,
0,
192,
63,
0,
0,
128,
255,
3,
0,
0,
0,
0,
0,
7,
0,
0,
0,
0,
0,
200,
51,
0,
0,
0,
0,
32,
0,
0,
0,
0,
0,
0,
0,
0,
126,
102,
0,
8,
16,
0,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
157,
193,
2,
0,
0,
0,
0,
48,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32,
33,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
127,
248,
255,
255,
255,
255,
255,
15,
0,
0,
0,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
0,
0,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
110,
240,
0,
0,
0,
0,
0,
135,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
0,
0,
0,
0,
0,
0,
0,
240,
0,
0,
0,
0,
0,
0,
0,
0,
62,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
0,
0,
0,
0,
0,
0,
0,
0,
0,
240,
0,
0,
0,
0,
0,
0,
0,
0,
192,
255,
1,
0,
0,
0,
0,
0,
60,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
255,
127,
0,
0,
0,
0,
0,
25,
128,
3,
0,
0,
0,
0,
0,
120,
38,
4,
32,
0,
0,
0,
0,
0,
0,
7,
0,
0,
0,
128,
239,
31,
0,
0,
0,
0,
0,
0,
0,
8,
0,
3,
0,
0,
0,
0,
0,
192,
127,
0,
158,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
211,
64,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
248,
7,
0,
0,
3,
0,
0,
0,
0,
0,
0,
24,
1,
0,
0,
0,
192,
31,
31,
0,
0,
0,
0,
0,
0,
0,
0,
248,
1,
64,
5,
0,
6,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
92,
0,
0,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
248,
133,
13,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
60,
176,
1,
0,
0,
48,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
248,
167,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
40,
191,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
160,
188,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
255,
6,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
88,
8,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
240,
12,
1,
0,
0,
0,
254,
7,
0,
0,
0,
0,
248,
121,
128,
0,
126,
14,
0,
0,
0,
0,
0,
252,
127,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
127,
191,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
252,
255,
255,
252,
109,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
126,
180,
191,
0,
0,
0,
0,
0,
0,
0,
0,
0,
163,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
0,
3,
0,
0,
0,
0,
0,
192,
7,
5,
0,
0,
4,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
129,
255,
63,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
255,
227,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
31,
0,
0,
0,
0,
0,
0,
0,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
128,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
63,
255,
255,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
3,
248,
255,
231,
15,
0,
0,
0,
60,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
28,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255,
255,
255,
127,
248,
255,
255,
255,
255,
255,
31,
32,
0,
16,
0,
0,
248,
254,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
127,
255,
255,
249,
219,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
0,
0,
0,
0,
0,
0,
0,
240,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
240,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
192,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
127,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
240,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0>>,
Cp
);
false ->
(Cp >= 16#e0000) andalso (Cp =< 16#e0fff)
end.
-file("src/string_width.gleam", 2242).
-spec wcwidth(options(), integer()) -> integer().
wcwidth(Options, Cp) ->
case Cp of
_ when Cp =< 16#1f ->
0;
_ when Cp =< 16#7e ->
1;
_ ->
case is_ignorable(Cp) of
true ->
0;
false ->
case is_wide(Cp) orelse (erlang:element(3, Options) andalso is_ambiguous(
Cp
)) of
true ->
2;
false ->
1
end
end
end.
-file("src/string_width.gleam", 2201).
-spec measure_mode_2027_ext(options(), binary()) -> integer().
measure_mode_2027_ext(Options, String) ->
string_width_ffi:fold_codepoints(
String,
0,
fun(Max, Cp) -> case Max =:= 0 of
true ->
wcwidth(Options, Cp);
false ->
case Cp of
16#fe0f ->
2;
_ ->
case wcwidth(Options, Cp) of
0 ->
Max;
Width ->
case is_spacing_mark(Cp) of
true ->
gleam@int:max(Max, Width);
false ->
2
end
end
end
end end
).
-file("src/string_width.gleam", 2323).
-spec fold_graphemes(binary(), DNB, fun((DNB, binary()) -> DNB)) -> DNB.
fold_graphemes(Str, State, Fun) ->
case gleam_stdlib:string_pop_grapheme(Str) of
{ok, {Grapheme, Str@1}} ->
fold_graphemes(Str@1, Fun(State, Grapheme), Fun);
{error, nil} ->
State
end.
-file("src/string_width.gleam", 2181).
-spec fold_chars_2027_ext(
options(),
binary(),
DNA,
fun((DNA, binary(), integer()) -> DNA)
) -> DNA.
fold_chars_2027_ext(Options, String, State, Fun) ->
fold_graphemes(
String,
State,
fun(State@1, Grapheme) ->
Width = measure_mode_2027_ext(Options, Grapheme),
Fun(State@1, Grapheme, Width)
end
).
-file("src/string_width.gleam", 2192).
-spec measure_mode_2027(options(), binary()) -> integer().
measure_mode_2027(Options, String) ->
string_width_ffi:fold_codepoints(String, 0, fun(Max, Cp) -> case Cp of
16#fe0f when Max > 0 ->
2;
_ ->
gleam@int:max(Max, wcwidth(Options, Cp))
end end).
-file("src/string_width.gleam", 2170).
-spec fold_chars_2027(
options(),
binary(),
DMZ,
fun((DMZ, binary(), integer()) -> DMZ)
) -> DMZ.
fold_chars_2027(Options, String, State, Fun) ->
fold_graphemes(
String,
State,
fun(State@1, Grapheme) ->
Width = measure_mode_2027(Options, Grapheme),
Fun(State@1, Grapheme, Width)
end
).
-file("src/string_width.gleam", 2146).
-spec fold_chars_raw(
options(),
binary(),
DMX,
fun((DMX, binary(), integer()) -> DMX)
) -> DMX.
fold_chars_raw(Options, String, State, Fun) ->
string_width_ffi:fold_codepoints(
String,
State,
fun(State@1, Cp) ->
Fun(
State@1,
string_width_ffi:utf_codepoint_to_string(Cp),
wcwidth(Options, Cp)
)
end
).
-file("src/string_width.gleam", 406).
?DOC(" Get the width of a tab at the given column.\n").
-spec tab(options(), integer()) -> integer().
tab(Options, Col) ->
(((case erlang:element(5, Options) of
0 -> 0;
Gleam@denominator -> Col div Gleam@denominator
end) + 1) * erlang:element(5, Options)) - Col.
-file("src/string_width.gleam", 413).
-spec prepare_measure(options(), binary()) -> {binary(),
list({integer(), integer()}),
integer()}.
prepare_measure(Options, Str) ->
Ansi_ranges = case erlang:element(2, Options) of
true ->
[];
false ->
string_width@internal@ansi:match(Str)
end,
{Str, Ansi_ranges, 0}.
-file("src/string_width.gleam", 367).
?DOC(" Like `dimensions`, but use custom options.\n").
-spec dimensions_with(binary(), options()) -> size().
dimensions_with(Str, Options) ->
{Str@1, Ranges, Range_width} = prepare_measure(Options, Str),
Fun = fun(State, Chr, Width) ->
{Rows, Cols_max, Cols_curr} = State,
case Chr of
<<"\n"/utf8>> ->
{Rows + 1,
gleam@int:max(Cols_max, Cols_curr),
erlang:element(6, Options)};
<<"\t"/utf8>> ->
{Rows, Cols_max, Cols_curr + tab(Options, Cols_curr)};
_ ->
{Rows, Cols_max, Cols_curr + Width}
end
end,
On_range = fun(State@1, Ansi, Length) ->
Fun(State@1, Ansi, Range_width * Length)
end,
On_chars = case erlang:element(4, Options) of
mode_wcwidth ->
fun(State@2, Str@2) ->
fold_chars_raw(Options, Str@2, State@2, Fun)
end;
mode2027 ->
fun(State@3, Str@3) ->
fold_chars_2027(Options, Str@3, State@3, Fun)
end;
mode2027_ext ->
fun(State@4, Str@4) ->
fold_chars_2027_ext(Options, Str@4, State@4, Fun)
end
end,
Initial = {0, 0, erlang:element(6, Options)},
{Rows@1, Cols_max@1, Cols_curr@1} = fold_parts(
Str@1,
0,
Ranges,
Initial,
On_chars,
On_range
),
Cols_max@2 = gleam@int:max(Cols_max@1, Cols_curr@1) - erlang:element(
6,
Options
),
Rows@2 = case Cols_curr@1 > erlang:element(6, Options) of
true ->
Rows@1 + 1;
false ->
Rows@1
end,
{size, Rows@2, Cols_max@2}.
-file("src/string_width.gleam", 323).
?DOC(
" Get the number of columns required to print a line in a terminal.\n"
"\n"
" If multiple lines are given, the length of the longest line is returned.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" line(\"äöüè\")\n"
" // --> 4\n"
"\n"
" line(\"안녕하세요\")\n"
" // --> 10\n"
"\n"
" line(\"👩👩👦👦\")\n"
" // --> 8\n"
"\n"
" line(\"\\u{1B}[31mhello\\u{1B}[39m\")\n"
" // --> 5\n"
" ```\n"
).
-spec line(binary()) -> integer().
line(Str) ->
{size, _, Columns} = dimensions_with(
Str,
{options, false, false, mode_wcwidth, 8, 0}
),
Columns.
-file("src/string_width.gleam", 341).
?DOC(
" Like `line`, but use custom options.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" let options =\n"
" new()\n"
" |> mode_2027\n"
"\n"
" line_with(\"👩👩👦👦\", options)\n"
" // --> 2\n"
" ```\n"
).
-spec line_with(binary(), options()) -> integer().
line_with(Str, Options) ->
{size, _, Columns} = dimensions_with(Str, Options),
Columns.
-file("src/string_width.gleam", 361).
?DOC(
" The required number of rows and columns to display the given text.\n"
"\n"
" The number of rows is equal to the number of lines, while the number of\n"
" columns represents the maximum line width.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" dimensions(\"안녕하세요\")\n"
" // --> Size(rows: 1, columns: 10)\n"
"\n"
" dimensions(\"hello,\\n안녕하세요\")\n"
" // --> Size(rows: 2, columns: 10)\n"
" ```\n"
).
-spec dimensions(binary()) -> size().
dimensions(Str) ->
dimensions_with(Str, {options, false, false, mode_wcwidth, 8, 0}).
-file("src/string_width.gleam", 752).
-spec limit_state_col(limit_state()) -> integer().
limit_state_col(State) ->
((erlang:element(5, State) + erlang:element(7, State)) + erlang:element(
9,
State
))
+ erlang:element(11, State).
-file("src/string_width.gleam", 1904).
?DOC(
" Returns true if a given component string recieved in `fold` is an ANSI\n"
" escape sequence.\n"
).
-spec is_ansi_component(binary(), options()) -> boolean().
is_ansi_component(Str, Options) ->
case erlang:element(2, Options) of
true ->
false;
false ->
case Str of
<<"\x{1b}"/utf8, _/binary>> ->
true;
<<"\x{9b}"/utf8, _/binary>> ->
true;
<<"\x{9d}"/utf8, _/binary>> ->
true;
_ ->
false
end
end.
-file("src/string_width.gleam", 2017).
?DOC(
" A low-level fold that iterate over the measured components of a string.\n"
" Components are either graphemes or codepoints depending on the `mode` option,\n"
" or other undivisible sequences, like ANSI escape codes.\n"
"\n"
" This function does not by itself keep track of any additional state, and\n"
" doesn't for example handle newlines or tabs like `fold` would.\n"
" If you know that your algorithm is safe and won't break up graphemes (or\n"
" maybe this is acceptable), you can use this fold variant instead as a\n"
" performance optimisation.\n"
"\n"
" Concatenating all components is guaranteed to produce the original string.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" // A slower string_width.line implementation that doesn't handle tabs\n"
" fold_raw(\"hello\", new(), from: 0, with: fn(so_far, _chr, width) { width + so_far })\n"
" // --> 5\n"
" ```\n"
"\n"
" ```gleam\n"
" // truncate a string after 50 characters, but keep all ansi sequences.\n"
" let options = new()\n"
" use #(total, acc), chr, width <- fold_raw(input, options, from: #(0, \"\"))\n"
" case total >= 50 {\n"
" True -> case is_ansi_component(chr, options) {\n"
" True -> #(total, acc <> chr)\n"
" False -> #(total, acc)\n"
" }\n"
" False -> #(total + width, acc <> chr)\n"
" }\n"
" ```\n"
).
-spec fold_raw(binary(), options(), DMS, fun((DMS, binary(), integer()) -> DMS)) -> DMS.
fold_raw(String, Options, State, Fun) ->
Ansi_ranges = case erlang:element(2, Options) of
true ->
[];
false ->
string_width@internal@ansi:match(String)
end,
On_chars = case erlang:element(4, Options) of
mode_wcwidth ->
fun(State@1, Str) -> fold_chars_raw(Options, Str, State@1, Fun) end;
mode2027 ->
fun(State@2, Str@1) ->
fold_chars_2027(Options, Str@1, State@2, Fun)
end;
mode2027_ext ->
fun(State@3, Str@2) ->
fold_chars_2027_ext(Options, Str@2, State@3, Fun)
end
end,
On_range = fun(State@4, Ansi, _) -> Fun(State@4, Ansi, 0) end,
fold_parts(String, 0, Ansi_ranges, State, On_chars, On_range).
-file("src/string_width.gleam", 784).
-spec limit_state_indent(fun((binary()) -> binary()), options(), limit_state()) -> {binary(),
integer()}.
limit_state_indent(Get_indent, Options, State) ->
case erlang:element(12, State) of
indent_unknown ->
Indent = Get_indent(erlang:element(3, State)),
Indent_width = line_with(Indent, Options),
{Indent, Indent_width};
{indent, Indent@1, Indent_width@1} ->
{Indent@1, Indent_width@1}
end.
-file("src/string_width.gleam", 2156).
-spec fold_chars_wcwidth(
options(),
binary(),
DMY,
fun((DMY, binary(), integer()) -> DMY)
) -> DMY.
fold_chars_wcwidth(Options, String, State, Fun) ->
fold_graphemes(
String,
State,
fun(State@1, Grapheme) ->
Width = begin
string_width_ffi:fold_codepoints(
Grapheme,
0,
fun(Acc, Cp) -> Acc + wcwidth(Options, Cp) end
)
end,
Fun(State@1, Grapheme, Width)
end
).
-file("src/string_width.gleam", 2082).
-spec piece_reducer(DMU, options(), fun((DMU, piece()) -> DMU)) -> {{DMU,
integer(),
integer()},
fun(({DMU, integer(), integer()}, binary(), integer()) -> {DMU,
integer(),
integer()})}.
piece_reducer(State, Options, Fun) ->
Initial = {State, 0, erlang:element(6, Options)},
Fun@1 = fun(State@1, Piece, Width) ->
{State@2, Row, Column} = State@1,
Width@1 = case Piece of
<<"\t"/utf8>> ->
tab(Options, Column);
_ ->
Width
end,
State@3 = Fun(State@2, {piece, Piece, Row, Column, Width@1}),
case Piece of
<<"\n"/utf8>> ->
{State@3, Row + 1, erlang:element(6, Options)};
_ ->
{State@3, Row, Column + Width@1}
end
end,
{Initial, Fun@1}.
-file("src/string_width.gleam", 1953).
?DOC(
" A higher-level `fold` that keeps track of the position inside of the string.\n"
" Like `fold`, but using custom options to measure the pieces.\n"
"\n"
" Handles tabs and newlines, and always passes full grapheme clusters,\n"
" regardless of options. Concatenating the graphemes produces the original string.\n"
"\n"
" Intended to be used as a basis for custom layout algorithms.\n"
).
-spec fold_with(binary(), options(), DMR, fun((DMR, piece()) -> DMR)) -> DMR.
fold_with(Str, Options, State, Fun) ->
{State@1, Fun@1} = piece_reducer(State, Options, Fun),
On_chars = case erlang:element(4, Options) of
mode_wcwidth ->
fun(State@2, Str@1) ->
fold_chars_wcwidth(Options, Str@1, State@2, Fun@1)
end;
mode2027 ->
fun(State@3, Str@2) ->
fold_chars_2027(Options, Str@2, State@3, Fun@1)
end;
mode2027_ext ->
fun(State@4, Str@3) ->
fold_chars_2027_ext(Options, Str@3, State@4, Fun@1)
end
end,
Ansi_ranges = case erlang:element(2, Options) of
true ->
[];
false ->
string_width@internal@ansi:match(Str)
end,
On_range = fun(State@5, Ansi, _) -> Fun@1(State@5, Ansi, 0) end,
{State@6, _, _} = fold_parts(
Str,
0,
Ansi_ranges,
State@1,
On_chars,
On_range
),
State@6.
-file("src/string_width.gleam", 769).
-spec limit_emergency_truncate(binary(), integer(), options()) -> {binary(),
integer()}.
limit_emergency_truncate(Str, Max_col, Options) ->
fold_with(
Str,
Options,
{<<""/utf8>>, 0},
fun(_use0, Piece) ->
{Truncated, Width} = _use0,
case is_ansi_component(erlang:element(2, Piece), Options) orelse ((erlang:element(
4,
Piece
)
+ erlang:element(5, Piece))
=< Max_col) of
true ->
{<>,
Width + erlang:element(5, Piece)};
false ->
{Truncated, Width}
end
end
).
-file("src/string_width.gleam", 1003).
?DOC(" Push a non-word character into LimitState, commiting the currently accumulated word.\n").
-spec limit_state_commit(
options(),
size(),
binary(),
integer(),
fun((binary()) -> binary()),
limit_state(),
binary(),
integer(),
boolean()
) -> limit_state().
limit_state_commit(
Options,
Max_size,
Ellipsis,
Ellipsis_width,
Get_indent,
State,
Piece,
Width,
Is_final
) ->
New_col = limit_state_col(State),
Max_row = erlang:element(2, Max_size),
Is_last_line = (erlang:element(4, State) + 1) >= Max_row,
Shy = Piece =:= <<"\x{ad}"/utf8>>,
Piece@1 = case Shy of
true ->
<<""/utf8>>;
false ->
Piece
end,
case {erlang:element(9, State) =:= 0, erlang:element(11, State) =:= 0} of
{true, true} ->
case (erlang:element(4, State) =:= 0) andalso (erlang:element(
5,
State
)
=:= 0) of
true ->
State;
false ->
{limit_state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
erlang:element(5, State),
<<(erlang:element(6, State))/binary, Piece@1/binary>>,
erlang:element(7, State) + Width,
erlang:element(8, State),
erlang:element(9, State),
erlang:element(10, State),
erlang:element(11, State),
erlang:element(12, State),
erlang:element(13, State)}
end;
{false, true} ->
Line = <<<<<<(erlang:element(3, State))/binary,
(erlang:element(6, State))/binary>>/binary,
(erlang:element(8, State))/binary>>/binary,
(erlang:element(10, State))/binary>>,
{limit_state,
erlang:element(2, State),
Line,
erlang:element(4, State),
New_col,
Piece@1,
Width,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
erlang:element(12, State),
Shy andalso ((New_col + 1) < erlang:element(3, Max_size))};
{_, false} ->
case {Is_last_line,
Is_final,
erlang:element(11, State) > Ellipsis_width} of
{true, true, false} ->
{limit_state,
<<<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
(erlang:element(6, State))/binary>>/binary,
(erlang:element(8, State))/binary>>/binary,
(erlang:element(10, State))/binary>>,
<<""/utf8>>,
erlang:element(4, State),
((erlang:element(5, State) + erlang:element(7, State)) + erlang:element(
9,
State
))
+ erlang:element(11, State),
Piece@1,
Width,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
erlang:element(12, State),
false};
{true, _, _} ->
{Truncated, Truncated_width} = limit_emergency_truncate(
<<<<(erlang:element(8, State))/binary,
(erlang:element(10, State))/binary>>/binary,
Piece@1/binary>>,
erlang:element(9, State),
Options
),
Str = case Truncated_width of
0 ->
<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
Truncated/binary>>/binary,
Ellipsis/binary>>;
_ ->
<<<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
(erlang:element(6, State))/binary>>/binary,
Truncated/binary>>/binary,
Ellipsis/binary>>
end,
{limit_state,
Str,
<<""/utf8>>,
erlang:element(4, State) + 1,
erlang:element(3, Max_size),
<<""/utf8>>,
0,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
indent_unknown,
false};
{false, _, _} ->
{Indent, Indent_width} = limit_state_indent(
Get_indent,
Options,
State
),
Str@1 = case erlang:element(13, State) of
true ->
<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
"-\n"/utf8>>;
false ->
<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
"\n"/utf8>>
end,
{limit_state,
Str@1,
<<<>/binary,
(erlang:element(10, State))/binary>>,
erlang:element(4, State) + 1,
(Indent_width + erlang:element(9, State)) + erlang:element(
11,
State
),
Piece@1,
Width,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
{indent, Indent, Indent_width},
Shy}
end
end.
-file("src/string_width.gleam", 756).
-spec limit_emergency_break_reflow(binary(), integer(), options()) -> {binary(),
binary(),
integer()}.
limit_emergency_break_reflow(Str, Max_col, Options) ->
Initial = {<<""/utf8>>, <<""/utf8>>, 0},
fold_with(
Str,
Options,
Initial,
fun(_use0, Piece) ->
{Before, After, Width} = _use0,
case (erlang:element(4, Piece) + erlang:element(5, Piece)) > Max_col of
true ->
{Before,
<>,
Width + erlang:element(5, Piece)};
false ->
{<>,
After,
Width}
end
end
).
-file("src/string_width.gleam", 800).
?DOC(" push a word-character into LimitState\n").
-spec limit_state_push(
options(),
size(),
binary(),
integer(),
fun((binary()) -> binary()),
limit_state(),
binary(),
integer()
) -> limit_state().
limit_state_push(
Options,
Max_size,
Ellipsis,
Ellipsis_width,
Get_indent,
State,
Piece,
Width
) ->
Line_col = limit_state_col(State) + Width,
Emergency_break_col = case erlang:element(12, State) of
indent_unknown ->
(erlang:element(9, State) + erlang:element(11, State)) + Width;
{indent, _, Indent_width} ->
((Indent_width + erlang:element(9, State)) + erlang:element(
11,
State
))
+ Width
end,
Lines_remaining = (erlang:element(2, Max_size) - erlang:element(4, State)) - 1,
Overflow_col = case Lines_remaining of
0 ->
erlang:element(3, Max_size) - Ellipsis_width;
_ ->
erlang:element(3, Max_size)
end,
case {Line_col =< Overflow_col,
(Emergency_break_col =< Overflow_col) orelse (Lines_remaining =:= 0)} of
{true, _} ->
{limit_state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
erlang:element(5, State),
erlang:element(6, State),
erlang:element(7, State),
<<(erlang:element(8, State))/binary, Piece/binary>>,
erlang:element(9, State) + Width,
erlang:element(10, State),
erlang:element(11, State),
erlang:element(12, State),
erlang:element(13, State)};
{false, true} ->
Indent = Get_indent(
<<<<(erlang:element(3, State))/binary,
(erlang:element(6, State))/binary>>/binary,
(erlang:element(8, State))/binary>>
),
Indent_width@1 = line_with(Indent, Options),
case (erlang:element(9, State) =:= 0) andalso (Lines_remaining > 0) of
true ->
{limit_state,
<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
(erlang:element(8, State))/binary>>/binary,
"\n"/utf8>>,
Indent,
erlang:element(4, State) + 1,
Indent_width@1,
<<""/utf8>>,
0,
<<(erlang:element(10, State))/binary, Piece/binary>>,
erlang:element(11, State) + Width,
<<""/utf8>>,
0,
{indent, Indent, Indent_width@1},
false};
false ->
{limit_state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
erlang:element(5, State),
erlang:element(6, State),
erlang:element(7, State),
erlang:element(8, State),
erlang:element(9, State),
<<(erlang:element(10, State))/binary, Piece/binary>>,
erlang:element(11, State) + Width,
{indent, Indent, Indent_width@1},
erlang:element(13, State)}
end;
{false, false} ->
{Indent@1, Indent_width@2} = limit_state_indent(
Get_indent,
Options,
State
),
case {Lines_remaining, erlang:element(13, State)} of
{1, true} ->
{Truncated, _} = limit_emergency_truncate(
<<<<(erlang:element(8, State))/binary,
(erlang:element(10, State))/binary>>/binary,
Piece/binary>>,
(erlang:element(3, Max_size) - Ellipsis_width) - Indent_width@2,
Options
),
{limit_state,
<<<<<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
"-\n"/utf8>>/binary,
Indent@1/binary>>/binary,
Truncated/binary>>/binary,
Ellipsis/binary>>,
<<""/utf8>>,
erlang:element(2, Max_size),
erlang:element(3, Max_size),
<<""/utf8>>,
0,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
{indent, Indent@1, Indent_width@2},
false};
{_, true} ->
{Before, After, After_width} = limit_emergency_break_reflow(
<<<<(erlang:element(8, State))/binary,
(erlang:element(10, State))/binary>>/binary,
Piece/binary>>,
erlang:element(3, Max_size) - Indent_width@2,
Options
),
{limit_state,
<<<<<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
"-\n"/utf8>>/binary,
Indent@1/binary>>/binary,
Before/binary>>/binary,
"\n"/utf8>>,
Indent@1,
erlang:element(4, State) + 2,
Indent_width@2,
<<""/utf8>>,
0,
After,
After_width,
<<""/utf8>>,
0,
{indent, Indent@1, Indent_width@2},
false};
{_, false} ->
{Truncated@1, Overflow, Overflow_width} = limit_emergency_break_reflow(
<<<<(erlang:element(8, State))/binary,
(erlang:element(10, State))/binary>>/binary,
Piece/binary>>,
erlang:element(9, State),
Options
),
{limit_state,
<<<<<<<<(erlang:element(2, State))/binary,
(erlang:element(3, State))/binary>>/binary,
(erlang:element(6, State))/binary>>/binary,
Truncated@1/binary>>/binary,
"\n"/utf8>>,
Indent@1,
erlang:element(4, State) + 1,
Indent_width@2,
<<""/utf8>>,
0,
Overflow,
Overflow_width,
<<""/utf8>>,
0,
{indent, Indent@1, Indent_width@2},
false}
end
end.
-file("src/string_width.gleam", 1794).
?DOC(" line_with, but with a few fast paths for commmonly used strings.\n").
-spec spacer_width(options(), binary()) -> integer().
spacer_width(Options, Str) ->
case Str of
<<""/utf8>> ->
0;
<<" "/utf8>> ->
1;
<<"..."/utf8>> ->
3;
<<"…"/utf8>> ->
1;
<<"0"/utf8>> ->
1;
_ ->
line_with(Str, Options)
end.
-file("src/string_width.gleam", 552).
?DOC(" Like `soft_wrap`, but using custom options to measure the strings and indents.\n").
-spec soft_wrap_with(
binary(),
size(),
options(),
binary(),
fun((binary()) -> binary())
) -> binary().
soft_wrap_with(Str, Max_size, Options, Ellipsis, To_indent) ->
Ellipsis_width = spacer_width(Options, Ellipsis),
Max_row = erlang:element(2, Max_size),
Push = fun(State, Piece, Width) ->
limit_state_push(
Options,
Max_size,
Ellipsis,
Ellipsis_width,
To_indent,
State,
Piece,
Width
)
end,
Commit = fun(State@1, Piece@1, Width@1, Is_final) ->
limit_state_commit(
Options,
Max_size,
Ellipsis,
Ellipsis_width,
To_indent,
State@1,
Piece@1,
Width@1,
Is_final
)
end,
Initial = {limit_state,
<<""/utf8>>,
<<""/utf8>>,
0,
0,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
<<""/utf8>>,
0,
indent_unknown,
false},
State@4 = begin
fold_raw(
Str,
Options,
Initial,
fun(State@2, Piece@2, Width@2) ->
case erlang:element(4, State@2) >= Max_row of
true ->
case is_ansi_component(Piece@2, Options) of
true ->
{limit_state,
erlang:element(2, State@2),
<<(erlang:element(3, State@2))/binary,
Piece@2/binary>>,
erlang:element(4, State@2),
erlang:element(5, State@2),
erlang:element(6, State@2),
erlang:element(7, State@2),
erlang:element(8, State@2),
erlang:element(9, State@2),
erlang:element(10, State@2),
erlang:element(11, State@2),
erlang:element(12, State@2),
erlang:element(13, State@2)};
false ->
State@2
end;
false ->
case Piece@2 of
<<"\n"/utf8>> ->
State@3 = Commit(State@2, <<""/utf8>>, 0, false),
{limit_state,
<<<<(erlang:element(2, State@3))/binary,
(erlang:element(3, State@3))/binary>>/binary,
"\n"/utf8>>,
<<""/utf8>>,
erlang:element(4, State@3) + 1,
0,
erlang:element(6, State@3),
erlang:element(7, State@3),
erlang:element(8, State@3),
erlang:element(9, State@3),
erlang:element(10, State@3),
erlang:element(11, State@3),
indent_unknown,
false};
<<"\t"/utf8>> ->
Curr_col = limit_state_col(State@2),
Width@3 = case Curr_col =< erlang:element(
3,
Max_size
) of
true ->
tab(
Options,
erlang:element(6, Options) + Curr_col
);
false ->
Wrapped_col = erlang:element(9, State@2)
+ erlang:element(11, State@2),
tab(
Options,
erlang:element(6, Options) + Wrapped_col
)
end,
Commit(State@2, Piece@2, Width@3, false);
<<" "/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{1680}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2000}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2001}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2002}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2003}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2004}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2005}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2006}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2008}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{2009}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{200a}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{205f}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{3000}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
<<"\x{ad}"/utf8>> ->
Commit(State@2, Piece@2, Width@2, false);
_ ->
Push(State@2, Piece@2, Width@2)
end
end
end
)
end,
State@5 = Commit(State@4, <<""/utf8>>, 0, true),
<<(erlang:element(2, State@5))/binary, (erlang:element(3, State@5))/binary>>.
-file("src/string_width.gleam", 459).
?DOC(" Like `limit`, but also customise the options used for measuring.\n").
-spec limit_with(binary(), size(), options(), binary()) -> binary().
limit_with(Str, Max_size, Options, Ellipsis) ->
soft_wrap_with(Str, Max_size, Options, Ellipsis, fun(_) -> <<""/utf8>> end).
-file("src/string_width.gleam", 449).
?DOC(
" Limit the dimensions of a string, either by wrapping on white space\n"
" characters or soft hyphens, or by truncating the last line and appending an\n"
" ellipsis.\n"
"\n"
" The lines of the resulting string are left aligned and are at most `columns`\n"
" wide. If a single word is longer than the maximum allowed line width, the\n"
" word is broken into 2 pieces at the grapheme level. If the string gets\n"
" truncated, this function will keep collecting ANSI sequences to make sure\n"
" all formatting is preserved.\n"
"\n"
" A commonly used ellipsis character is `\"…\"`, also known as\n"
" U+2026 HORIZONTAL ELLIPSIS.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" limit(\"Hello World\", Size(rows: 1, columns: 10), ellipsis: \"...\")\n"
" // --> \"Hello W...\"\n"
"\n"
" limit(\"Hello World\", Size(rows: 2, columns: 10), ellipsis: \"...\")\n"
" // --> \"Hello\\nWorld\"\n"
" ```\n"
).
-spec limit(binary(), size(), binary()) -> binary().
limit(Str, Max_size, Ellipsis) ->
limit_with(
Str,
Max_size,
{options, false, false, mode_wcwidth, 8, 0},
Ellipsis
).
-file("src/string_width.gleam", 539).
?DOC(
" Limit the dimensions of a string, either by wrapping on white space\n"
" characters or soft hyphens while using a custom indentation function, or by\n"
" truncating the last line and appending an ellipsis.\n"
"\n"
" This function gives a little bit more control over what happens on soft\n"
" line breaks than `limit` and allows you to inspect the line printed so far\n"
" to figure out the indentation for all soft-wrapped lines. The `to_indent`\n"
" callback is called with the line so far, and should return an indentation\n"
" string (usually just the proper amount of spaces) that is prepended to all\n"
" the following soft-wrapped lines.\n"
"\n"
" Behaves like `limit` otherwise.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" fn to_indent(str) {\n"
" case str {\n"
" // if it is a list, indent with 2 spaces\n"
" \"- \" <> _ -> \" \"\n"
" // if it is a blockquote, prepend another blockquote marker\n"
" \"> \" <> _ -> \"> \"\n"
" // else, don't indent (like limit)\n"
" _ -> \"\"\n"
" }\n"
" }\n"
"\n"
" // Girly Girl Productions - 10 drunk cigarettes\n"
" let lyrics = \"\n"
" > Getting girls rich, yeah, that's a part of my plan.\n"
" > And I could name 10 things us girls need, before we ever need a man:\n"
"\n"
" - One new vape, two lines of coke\n"
" - Three drinks from the bar, four more lines of coke\n"
" - Five Guys fries, six hits from my blunt\n"
" - Seven more lines of coke, eight pairs of shoes\n"
" - Nine BB belts, and 10 drunk cigarettes.\n"
" \"\n"
"\n"
" lyrics\n"
" |> soft_wrap(to: Size(rows: 24, columns: 34), ellipsis: \"...\", to_indent:)\n"
" |> io.println\n"
" ```\n"
"\n"
" **Output:**\n"
"\n"
" ```txt\n"
" > Getting girls rich, yeah, that's\n"
" > a part of my plan.\n"
" > And I could name 10 things us\n"
" > girls need, before we ever need\n"
" > a man:\n"
"\n"
" - One new vape, two lines of coke\n"
" - Three drinks from the bar, four\n"
" more lines of coke\n"
" - Five Guys fries, six hits from\n"
" my blunt\n"
" - Seven more lines of coke, eight\n"
" pairs of shoes\n"
" - Nine BB belts, and 10 drunk\n"
" cigarettes.\n"
" ```\n"
"\n"
" **Note:** This is not meant to allow for arbitrary text formatting, but as a\n"
" quick way to indicate soft-wrapping in the printed text. As shown in the\n"
" example, it _can_ be used to do some very light formatting, but you should\n"
" almost always parse the string first, and print it in a way that makes sense\n"
" for your text format, for example using the awesome\n"
" [glam](https://hexdocs.pm/glam/) package!\n"
).
-spec soft_wrap(binary(), size(), binary(), fun((binary()) -> binary())) -> binary().
soft_wrap(Str, Max_size, Ellipsis, To_indent) ->
soft_wrap_with(
Str,
Max_size,
{options, false, false, mode_wcwidth, 8, 0},
Ellipsis,
To_indent
).
-file("src/string_width.gleam", 2053).
?DOC(
" A version of `fold_raw` that does keep track of some state for you to\n"
" handle tabs, and count the current row/column, passing `Piece` values to\n"
" you instead.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" fold_raw_pieces(\"hi!\", using: new(), from: \"\", with: list.prepend)\n"
" |> list.reverse\n"
" // --> [Piece(\"h\", 0, 0, 1), Piece(\"i\", 0, 1, 1), Piece(\"!\", 0, 2, 1)]\n"
" ```\n"
).
-spec fold_raw_pieces(binary(), options(), DMT, fun((DMT, piece()) -> DMT)) -> DMT.
fold_raw_pieces(String, Options, State, Fun) ->
Ansi_ranges = case erlang:element(2, Options) of
true ->
[];
false ->
string_width@internal@ansi:match(String)
end,
{State@1, Fun@1} = piece_reducer(State, Options, Fun),
On_chars = case erlang:element(4, Options) of
mode_wcwidth ->
fun(State@2, Str) ->
fold_chars_raw(Options, Str, State@2, Fun@1)
end;
mode2027 ->
fun(State@3, Str@1) ->
fold_chars_2027(Options, Str@1, State@3, Fun@1)
end;
mode2027_ext ->
fun(State@4, Str@2) ->
fold_chars_2027_ext(Options, Str@2, State@4, Fun@1)
end
end,
On_range = fun(State@5, Ansi, _) -> Fun@1(State@5, Ansi, 0) end,
{State@6, _, _} = fold_parts(
String,
0,
Ansi_ranges,
State@1,
On_chars,
On_range
),
State@6.
-file("src/string_width.gleam", 1206).
?DOC(
" Like `tabs_to_spaces`, but customise the opttions as well.\n"
"\n"
" **NB:** You can use `at_tab_offset` and `with_tab_width` to change the measure of\n"
" tab characters inside the string!\n"
"\n"
" ```gleam\n"
" let options = new() |> with_tab_width(4)\n"
" tabs_to_spaces_with(\"hi\\tcutie~\", options)\n"
" // --> \"hi cutie~\"\n"
" ```\n"
).
-spec tabs_to_spaces_with(binary(), options()) -> binary().
tabs_to_spaces_with(Str, Options) ->
fold_raw_pieces(
Str,
Options,
<<""/utf8>>,
fun(Acc, Piece) -> case erlang:element(2, Piece) of
<<"\t"/utf8>> ->
<>,
erlang:element(5, Piece)
))/binary>>;
_ ->
<>
end end
).
-file("src/string_width.gleam", 1191).
?DOC(
" Replace all tab characters found in the string with the amount of spaces\n"
" that this tab would have had otherwise.\n"
"\n"
" This makes it safe to prepend to a line, without changing the spacing produced\n"
" by tabs anymore.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" tabs_to_spaces(\"Hello\\tWorld\")\n"
" // --> \"Hello World\" // 3 spaces\n"
" ```\n"
).
-spec tabs_to_spaces(binary()) -> binary().
tabs_to_spaces(Str) ->
tabs_to_spaces_with(Str, {options, false, false, mode_wcwidth, 8, 0}).
-file("src/string_width.gleam", 1781).
?DOC(" Integer division, rounding up\n").
-spec div_up(integer(), integer()) -> integer().
div_up(Numerator, Denom) ->
case Denom of
0 -> 0;
Gleam@denominator -> ((Numerator + Denom) - 1) div Gleam@denominator
end.
-file("src/string_width.gleam", 1786).
?DOC(" do something, multiple times.\n").
-spec repeat(integer(), DMK, fun((DMK) -> DMK)) -> DMK.
repeat(Times, State, Fun) ->
case Times > 0 of
true ->
repeat(Times - 1, Fun(State), Fun);
false ->
State
end.
-file("src/string_width.gleam", 1692).
?DOC(
" collect a viewport/cutout of a string into a custom data structure.\n"
" push is supposed to pad/align/etc however it sees fit.\n"
" `stack_horizontal` uses this to collect all lines in parallel.\n"
).
-spec view_fold(
binary(),
integer(),
integer(),
integer(),
integer(),
options(),
DMI,
fun((DMI, binary(), integer()) -> DMI)
) -> DMI.
view_fold(Str, Top, Left, Bottom, Right, Options, Initial, Push) ->
Total_height = gleam@int:max(0, Bottom - Top),
Line_width_offset = gleam@int:max(0, Left),
Push_line = fun(State) ->
case (erlang:element(4, State) < Top) orelse (erlang:element(4, State)
>= Bottom) of
true ->
{view_state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State) + 1,
0};
false ->
Acc = Push(
erlang:element(2, State),
erlang:element(3, State),
erlang:element(5, State) - Line_width_offset
),
{view_state, Acc, <<""/utf8>>, erlang:element(4, State) + 1, 0}
end
end,
Acc@1 = repeat(
gleam@int:min(- Top, Total_height),
Initial,
fun(_capture) -> Push(_capture, <<""/utf8>>, 0) end
),
State@2 = begin
fold_raw(
Str,
Options,
{view_state, Acc@1, <<""/utf8>>, 0, 0},
fun(State@1, Piece, Width) ->
Width@1 = case Piece of
<<"\t"/utf8>> ->
tab(
Options,
erlang:element(5, State@1) + erlang:element(
6,
Options
)
);
_ ->
Width
end,
case Piece of
<<"\n"/utf8>> ->
Push_line(State@1);
_ ->
case (((erlang:element(4, State@1) < Top) orelse (erlang:element(
4,
State@1
)
>= Bottom))
orelse (erlang:element(5, State@1) < Left))
orelse (erlang:element(5, State@1) >= Right) of
true ->
case is_ansi_component(Piece, Options) of
true ->
{view_state,
erlang:element(2, State@1),
<<(erlang:element(3, State@1))/binary,
Piece/binary>>,
erlang:element(4, State@1),
erlang:element(5, State@1)};
false ->
{view_state,
erlang:element(2, State@1),
erlang:element(3, State@1),
erlang:element(4, State@1),
erlang:element(5, State@1) + Width@1}
end;
false ->
{view_state,
erlang:element(2, State@1),
<<(erlang:element(3, State@1))/binary,
Piece/binary>>,
erlang:element(4, State@1),
erlang:element(5, State@1) + Width@1}
end
end
end
)
end,
Acc@2 = case (erlang:element(4, State@2) >= Top) andalso (erlang:element(
4,
State@2
)
< Bottom) of
true ->
Push(
erlang:element(2, State@2),
erlang:element(3, State@2),
erlang:element(5, State@2) - Line_width_offset
);
false ->
Push(erlang:element(2, State@2), erlang:element(3, State@2), -1)
end,
Missing_bottom = case erlang:element(5, State@2) >= Line_width_offset of
true ->
(Bottom - erlang:element(4, State@2)) - 1;
false ->
Bottom - erlang:element(4, State@2)
end,
repeat(
gleam@int:min(Missing_bottom, Total_height),
Acc@2,
fun(_capture@1) -> Push(_capture@1, <<""/utf8>>, 0) end
).
-file("src/string_width.gleam", 1665).
?DOC(
" cutout a viewport of a string.\n"
" the passed align function can pad the string to the right length, etc.\n"
" `align` uses a different align function to box.\n"
).
-spec view(
binary(),
integer(),
integer(),
integer(),
integer(),
options(),
fun((binary(), integer()) -> binary())
) -> binary().
view(Str, Top, Left, Bottom, Right, Options, Align) ->
Push = fun(Buf, Line, Line_width) -> case Line_width >= 0 of
true ->
<<<>/binary,
(Align(Line, Line_width))/binary>>;
false ->
<>
end end,
Result = view_fold(
Str,
Top,
Left,
Bottom,
Right,
Options,
<<""/utf8>>,
Push
),
case Result of
<<"\n"/utf8, Result@1/binary>> ->
Result@1;
_ ->
Result
end.
-file("src/string_width.gleam", 1634).
?DOC(
" cutout a viewport of a string in a rectangular manner, filling the remaining\n"
" space with the spacer. Internal version of scroll or padding.\n"
).
-spec box(
binary(),
integer(),
integer(),
integer(),
integer(),
options(),
binary()
) -> binary().
box(Str, Top, Left, Height, Width, Options, Space) ->
Space_width = spacer_width(Options, Space),
Missing_left = div_up(gleam@int:max(0, - Left), Space_width),
Padding_left = gleam@string:repeat(Space, Missing_left),
Bottom = Top + Height,
Right = Left + Width,
view(
Str,
Top,
Left,
Bottom,
Right,
Options,
fun(Line, Line_width) ->
Missing_right = div_up(
(Width - Missing_left) - Line_width,
Space_width
),
Padding_right = gleam@string:repeat(Space, Missing_right),
<<<>/binary,
Padding_right/binary>>
end
).
-file("src/string_width.gleam", 1260).
?DOC(" Like `position`, but use custom options to measure each line.\n").
-spec position_with(
binary(),
size(),
alignment(),
placement(),
options(),
binary()
) -> binary().
position_with(Str, Bounding_box, Alignment, Placement, Options, Space) ->
Size = dimensions_with(Str, Options),
Left = case Alignment of
left ->
0;
right ->
gleam@int:min(
erlang:element(3, Size) - erlang:element(3, Bounding_box),
0
);
center ->
gleam@int:min(
(erlang:element(3, Size) - erlang:element(3, Bounding_box)) div 2,
0
)
end,
Top = case Placement of
top ->
0;
bottom ->
gleam@int:min(
erlang:element(2, Size) - erlang:element(2, Bounding_box),
0
);
middle ->
gleam@int:min(
(erlang:element(2, Size) - erlang:element(2, Bounding_box)) div 2,
0
)
end,
Width = gleam@int:max(
erlang:element(3, Size),
erlang:element(3, Bounding_box)
),
Height = gleam@int:max(
erlang:element(2, Size),
erlang:element(2, Bounding_box)
),
box(Str, Top, Left, Height, Width, Options, Space).
-file("src/string_width.gleam", 1248).
?DOC(
" Position the string area inside a bigger box without changing text alignment.\n"
" The box will be filled with the space character.\n"
"\n"
" **Tip:** If you only want to align a string on one axis, you can provide\n"
" `0` for the orthogonal one! Extra characters and lines will be kept.\n"
"\n"
" If the space strings' width does not evenly divide the missing amount of\n"
" columns, the extra spacer will overflow the max width.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" position(\"X\", in: Size(3, 3), align: Center, place: Middle, with: \"o\")\n"
" // --> \"ooo\\noXo\\nooo\"\n"
"\n"
" // text still left-aligned, but moved to the right by 1 space\n"
" position(\"Trans\\nrights\\nare\\nhuman\\nrights\", Size(0, 7), Right, Top, \" \")\n"
" // --> \" Trans \\n rights\\n are \\n human \\n rights\"\n"
" ```\n"
).
-spec position(binary(), size(), alignment(), placement(), binary()) -> binary().
position(Str, Bounding_box, Alignment, Placement, Space) ->
position_with(
Str,
Bounding_box,
Alignment,
Placement,
{options, false, false, mode_wcwidth, 8, 0},
Space
).
-file("src/string_width.gleam", 1325).
?DOC(" Like `align`, bu use custom options for measure.\n").
-spec align_with(binary(), integer(), alignment(), options(), binary()) -> binary().
align_with(Str, Max_width, Alignment, Options, Space) ->
Space_width = spacer_width(Options, Space),
Str_size = dimensions_with(Str, Options),
Bottom = gleam@int:max(1, erlang:element(2, Str_size)),
Right = gleam@int:max(erlang:element(3, Str_size), Max_width),
view(
Str,
0,
0,
Bottom,
Right,
Options,
fun(Line, Line_width) ->
Missing = div_up(Max_width - Line_width, Space_width),
case Alignment of
left ->
<>;
right ->
<<(gleam@string:repeat(Space, Missing))/binary,
Line/binary>>;
center ->
Left = Missing div 2,
Right@1 = Missing - Left,
<<<<(gleam@string:repeat(Space, Left))/binary, Line/binary>>/binary,
(gleam@string:repeat(Space, Right@1))/binary>>
end
end
).
-file("src/string_width.gleam", 1314).
?DOC(
" Align each line horizontally, using a spacer character or string as a filler.\n"
" Each line of the return value will be at least `max_width` wide.\n"
"\n"
" If a line is already longer than the max width, it will not be changed.\n"
" If the space strings width does not evenly divide the missing amount of\n"
" columns, the extra spacer will overflow the max width.\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" align(\"\", to: 5, align: Left, with: \"x\")\n"
" // --> \"xxxxx\"\n"
"\n"
" align(\"hello\", to: 10, align: Left, with: \" \")\n"
" // --> \"hello \"\n"
"\n"
" align(\"12345\", to: 8, align: Right, with: \"0\")\n"
" // --> \"00012345\"\n"
"\n"
" align(\" Welcome \", to: 20, align: Center, with: \"==\")\n"
" // --> \"====== Welcome ======\" // (len = 21)\n"
"\n"
" align(\"Trans\\nrights\\nare\\nhuman\\nrights\", to: 7, align: Right, with: \" \")\n"
" // --> \" Trans\\n rights\\n are\\n human\\n rights\"\n"
" ```\n"
).
-spec align(binary(), integer(), alignment(), binary()) -> binary().
align(Str, Max_width, Alignment, Space) ->
align_with(
Str,
Max_width,
Alignment,
{options, false, false, mode_wcwidth, 8, 0},
Space
).
-file("src/string_width.gleam", 1378).
?DOC(
" Add some padding around the box of a string, filling empty space and the\n"
" padded area with the provided character.\n"
"\n"
" Padding is given as a number of rows/columns. Values are given clockwise,\n"
" following the CSS shorthand property.\n"
"\n"
" Ansi sequences will always be kept. If the space strings' width does not\n"
" evenly divide the missing area in the computed outer size of the string,\n"
" the extra spacer will the resulting lines may not all be equally long.\n"
"\n"
" **Tip:** Provide negative values to drop columns or rows!\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" padding(\"o\", 1, 1, 1, 1, with: \"x\")\n"
" // --> \"xxx\\nxox\\nxxx\"\n"
"\n"
" padding(\"hello\", 0, 0, 0, right: -1, with: \"\")\n"
" // --> \"hell\"\n"
"\n"
" padding(\"Trans\\nrights!\", top: 2, right: 5, bottom: 1, left: 1, with: \"-\")\n"
" // --> \"-------------\\n-------------\\n-Trans-------\\n-rights!-----\\n-------------\"\n"
" ```\n"
).
-spec padding(binary(), integer(), integer(), integer(), integer(), binary()) -> binary().
padding(Str, Top, Right, Bottom, Left, Space) ->
Options = {options, false, false, mode_wcwidth, 8, 0},
Size = dimensions_with(Str, Options),
Width = (erlang:element(3, Size) + Left) + Right,
Height = (erlang:element(2, Size) + Top) + Bottom,
box(Str, - Top, - Left, Height, Width, Options, Space).
-file("src/string_width.gleam", 1397).
?DOC(" Like `padding`, but use custom options for measuring.\n").
-spec padding_with(
binary(),
integer(),
integer(),
integer(),
integer(),
options(),
binary()
) -> binary().
padding_with(Str, Top, Right, Bottom, Left, Options, Space) ->
Size = dimensions_with(Str, Options),
Width = (erlang:element(3, Size) + Left) + Right,
Height = (erlang:element(2, Size) + Top) + Bottom,
box(Str, - Top, - Left, Height, Width, Options, Space).
-file("src/string_width.gleam", 1435).
?DOC(
" Cut out a `viewort` at the given origin `#(top, left)` from a string. This\n"
" is similar to _\"scrolling\"_ a web page.\n"
"\n"
" You can scroll past the filled area of the string in any direction.\n"
" Empty space is filled using the `space` string. If the space string does not\n"
" evenly fill the empty area, some lines may be longer than the given viewport.\n"
"\n"
" All ANSI sequences in the entire string will be kept, even if they are\n"
" outside of the visible viewport.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" scroll(\"wibble\", top: 0, left: 2, view: Size(1, 2), with: \" \")\n"
" // --> \"bb\"\n"
"\n"
" \"Hello\\nHow are you today?\\nDid you know:\\nLucy says trans rights!\"\n"
" |> scroll(top: 2, left: 10, view: Size(3, 5), with: \"-\")\n"
" // --> \"ow:--\\ntrans\\n-----\"\n"
" ```\n"
).
-spec scroll(binary(), integer(), integer(), size(), binary()) -> binary().
scroll(Str, Top, Left, Viewport_size, Space) ->
Options = {options, false, false, mode_wcwidth, 8, 0},
{size, Height, Width} = Viewport_size,
box(Str, Top, Left, Height, Width, Options, Space).
-file("src/string_width.gleam", 1449).
?DOC(" Like `scroll`, but provide custom options for measuring.\n").
-spec scroll_with(binary(), integer(), integer(), size(), options(), binary()) -> binary().
scroll_with(Str, Top, Left, Viewport_size, Options, Space) ->
{size, Height, Width} = Viewport_size,
box(Str, Top, Left, Height, Width, Options, Space).
-file("src/string_width.gleam", 1806).
?DOC(" early return on empty lists.\n").
-spec guard_list(list(DMM), DMO, fun((DMM, list(DMM)) -> DMO)) -> DMO.
guard_list(List, Empty, Non_empty) ->
case List of
[] ->
Empty;
[Head | Tail] ->
Non_empty(Head, Tail)
end.
-file("src/string_width.gleam", 1490).
?DOC(" Like `stack_vertical`, but using custom options for measure.\n").
-spec stack_vertical_with(
list(binary()),
alignment(),
integer(),
options(),
binary()
) -> binary().
stack_vertical_with(Blocks, Horizontal, Gap, Options, Space) ->
guard_list(
Blocks,
<<""/utf8>>,
fun(First, Blocks@1) ->
First_size = dimensions_with(First, Options),
Measured = begin
gleam@list:map(
Blocks@1,
fun(Block) -> {Block, dimensions_with(Block, Options)} end
)
end,
Width@1 = begin
gleam@list:fold(
Measured,
erlang:element(3, First_size),
fun(Max, _use1) ->
{_, {size, _, Width}} = _use1,
gleam@int:max(Max, Width)
end
)
end,
Space_width = spacer_width(Options, Space),
Gap_line = gleam@string:repeat(Space, div_up(Width@1, Space_width)),
Gap_str = gleam@string:repeat(<<"\n"/utf8, Gap_line/binary>>, Gap),
Render = fun(Block@1, Size) ->
Left = case Horizontal of
left ->
0;
right ->
erlang:element(3, Size) - Width@1;
center ->
(erlang:element(3, Size) - Width@1) div 2
end,
box(
Block@1,
0,
Left,
erlang:element(2, Size),
Width@1,
Options,
Space
)
end,
gleam@list:fold(
Measured,
Render(First, First_size),
fun(Buf, _use1@1) ->
{Block@2, Size@1} = _use1@1,
<<<<<>/binary, "\n"/utf8>>/binary,
(Render(Block@2, Size@1))/binary>>
end
)
end
).
-file("src/string_width.gleam", 1479).
?DOC(
" Stack multiple blocks of strings on top of each other into multiple rows.\n"
"\n"
" The resulting string will as wide as the widest string in the list. All other\n"
" blocks can be `position`-ed horizontally relative to this size.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" [\n"
" \"Hello!\",\n"
" \"I hope you are feeling fantastic today!\"\n"
" ]\n"
" |> stack_vertical(Center, gap: 1, with: \" \")\n"
" // --> \" Hello! \\n\"\n"
" // <> \" \\n\"\n"
" // <> \"I hope you are feeling fantastic today!\"\n"
" ```\n"
).
-spec stack_vertical(list(binary()), alignment(), integer(), binary()) -> binary().
stack_vertical(Blocks, Horizontal, Gap, Space) ->
stack_vertical_with(
Blocks,
Horizontal,
Gap,
{options, false, false, mode_wcwidth, 8, 0},
Space
).
-file("src/string_width.gleam", 1560).
?DOC(" Like `stack_horizontal`, but using custom options for measure.\n").
-spec stack_horizontal_with(
list(binary()),
placement(),
integer(),
options(),
binary()
) -> binary().
stack_horizontal_with(Blocks, Vertical, Gap, Options, Space) ->
guard_list(
Blocks,
<<""/utf8>>,
fun(First, Blocks@1) ->
First_size = dimensions_with(First, Options),
Measured = begin
gleam@list:map(
Blocks@1,
fun(Block) -> {Block, dimensions_with(Block, Options)} end
)
end,
Height@1 = gleam@int:max(
1,
begin
gleam@list:fold(
Measured,
erlang:element(2, First_size),
fun(Max, _use1) ->
{_, {size, Height, _}} = _use1,
gleam@int:max(Max, Height)
end
)
end
),
Space_width = spacer_width(Options, Space),
Gap_str = gleam@string:repeat(Space, div_up(Gap, Space_width)),
Render = fun(Lines, Block@1, Size, Push_column) ->
Push = fun(State, Line, Line_width) -> case State of
{[Line_so_far | Rest_input], Output} ->
Missing_right = div_up(
erlang:element(3, Size) - Line_width,
Space_width
),
Padding_right = gleam@string:repeat(
Space,
Missing_right
),
Line@1 = Push_column(
Line_so_far,
<>
),
{Rest_input, [Line@1 | Output]};
_ ->
State
end end,
Top = case Vertical of
top ->
0;
bottom ->
erlang:element(2, Size) - Height@1;
middle ->
(erlang:element(2, Size) - Height@1) div 2
end,
Bottom = Top + Height@1,
Right = erlang:element(3, Size),
{_, Formatted_lines} = view_fold(
Block@1,
Top,
0,
Bottom,
Right,
Options,
{Lines, []},
Push
),
lists:reverse(Formatted_lines)
end,
Lines@1 = begin
Render(
gleam@list:repeat(<<""/utf8>>, Height@1),
First,
First_size,
fun(_, Line@2) -> Line@2 end
)
end,
Lines@3 = begin
gleam@list:fold(
Measured,
Lines@1,
fun(Lines@2, _use1@1) ->
{Block@2, Size@1} = _use1@1,
Render(
Lines@2,
Block@2,
Size@1,
fun(Line_so_far@1, Line@3) ->
<<<>/binary,
Line@3/binary>>
end
)
end
)
end,
gleam@string:join(Lines@3, <<"\n"/utf8>>)
end
).
-file("src/string_width.gleam", 1549).
?DOC(
" Stack multiple blocks of strings horizontally next to each other into\n"
" multiple columns.\n"
"\n"
" The resulting string will as high as the highest string in the list. All\n"
" other blocks can be `position`-ed vertically relative to this size.\n"
"\n"
" **Tip;** Use [limit](#limit) and [inline_styles](#inline_styles) to lay out\n"
" the text in your columns!\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" [\n"
" \"--color\",\n"
" \"Enable color support.\\nThis option is enabled by default in a terminal.\"\n"
" ]\n"
" |> stack_horizontal(place: Top, gap: 4, with: \" \")\n"
" // --> \"--color Enable color support. \\n\"\n"
" // <> \" This option is enabled by default in a terminal.\"\n"
" ```\n"
).
-spec stack_horizontal(list(binary()), placement(), integer(), binary()) -> binary().
stack_horizontal(Blocks, Vertical, Gap, Space) ->
stack_horizontal_with(
Blocks,
Vertical,
Gap,
{options, false, false, mode_wcwidth, 8, 0},
Space
).
-file("src/string_width.gleam", 1836).
?DOC(
" Make sure [SGR ansi codes](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters)\n"
" (these are the ones you'd use for colors and styling!) never wrap\n"
" around a line. This makes it save to use other layout functions on the\n"
" resulting string, without affecting the styling anymore.\n"
"\n"
" It does so by collecting all SGR sequences since the last reset, and\n"
" re-applying them on each line break.\n"
"\n"
" **Warning:** This function assumes it is safe to emit reset commands,\n"
" and that no ambient styling is in effect (that is, the terminal would be in\n"
" its default state when printing the resulting string!)\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" // \"\\u{1b}[31m\": red text color\n"
" // \"\\u{1b}[m\": reset/normal mode\n"
" inline_styles(\"\\u{1b}[31mhello\\nworld\")\n"
" // --> \"\\u{1b}[31mhello\" <> \"\\u{1b}[m\\n\" <> \"\\u{1b}[31mworld\" <> \"\\u{1b}[m\"\n"
" ```\n"
).
-spec inline_styles(binary()) -> binary().
inline_styles(Str) ->
Ansi_ranges = string_width@internal@ansi:match(Str),
On_range = fun(State, Ansi, _) ->
{Buf, Sgr} = State,
case Ansi of
<<"\x{1b}[0m"/utf8>> ->
{<>, <<""/utf8>>};
<<"\x{1b}[m"/utf8>> ->
{<>, <<""/utf8>>};
_ ->
case gleam_stdlib:string_ends_with(Ansi, <<"m"/utf8>>) of
true ->
{<>,
<>};
false ->
{<>, Sgr}
end
end
end,
On_chars = fun(State@1, Chars) -> case State@1 of
{Buf@1, <<""/utf8>>} ->
{<>, <<""/utf8>>};
{Buf@2, Sgr@1} ->
{Rest, _, Buf@4} = begin
string_width_ffi:fold_bytes(
Chars,
{Chars, 0, Buf@2},
fun(_use0, Byte) ->
{Ptr, Pos, Buf@3} = _use0,
case Byte of
16#A ->
{Line@1, Ptr@2} = case erlang:split_binary(
Ptr,
Pos
) of
{Line, <<"\n"/utf8, Ptr@1/binary>>} -> {
Line,
Ptr@1};
_assert_fail ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <>,
module => <<"string_width"/utf8>>,
function => <<"inline_styles"/utf8>>,
line => 1859,
value => _assert_fail,
start => 59210,
'end' => 59266,
pattern_start => 59221,
pattern_end => 59241}
)
end,
{Ptr@2,
0,
<<<<<>/binary,
"\x{1b}[m\n"/utf8>>/binary,
Sgr@1/binary>>};
_ ->
{Ptr, Pos + 1, Buf@3}
end
end
)
end,
{<>, Sgr@1}
end end,
State@2 = fold_parts(
Str,
0,
Ansi_ranges,
{<<""/utf8>>, <<""/utf8>>},
On_chars,
On_range
),
case State@2 of
{Buf@5, <<""/utf8>>} ->
Buf@5;
{Buf@6, _} ->
<>
end.
-file("src/string_width.gleam", 1897).
?DOC(
" Strip _all_ ansi sequences from a string, using the same matching algorithm\n"
" that this library uses internally.\n"
"\n"
" This makes it safe to enable `count_ansi_codes`, or print a string without\n"
" having to worry that it might mess with the terminal and/or colors.\n"
"\n"
" Unexpected characters inside of escape sequences are ignored (swallowed).\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" strip_ansi(\"\\u{1b}[0;33;49;3;9;4mhi~\\u{1b}[0m\")\n"
" // --> \"hi~\"\n"
"\n"
" strip_ansi(\"check out \\u{1b}]8;;https://gleam.run\\u{7}Gleam!\\u{1b}]8;;\\u{7}\")\n"
" // --> \"check out Gleam!\"\n"
" ```\n"
).
-spec strip_ansi(binary()) -> binary().
strip_ansi(Str) ->
string_width@internal@ansi:strip(Str).
-file("src/string_width.gleam", 1937).
?DOC(
" A higher-level `fold` that keeps track of the position inside of the string.\n"
"\n"
" Handles tabs and newlines, and always passes full grapheme clusters,\n"
" regardless of options. Concatenating the graphemes produces the original string.\n"
"\n"
" Intended to be used as a basis for custom layout algorithms.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" fold(\"hi 😊\", from: [], with: list.prepend)\n"
" |> list.reverse\n"
" // --> [Piece(\"h\", 0, 0, 1), Piece(\"i\", 0, 1, 1), Piece(\" \", 0, 2, 1), Piece(\"😊\", 0, 3, 2)]\n"
" ```\n"
).
-spec fold(binary(), DMQ, fun((DMQ, piece()) -> DMQ)) -> DMQ.
fold(Str, State, Fun) ->
fold_with(Str, {options, false, false, mode_wcwidth, 8, 0}, State, Fun).