diff --git a/README.md b/README.md index 2f10af5..e0aefda 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ pub fn main() { string_width.line_with( "πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦", - string_width.new() |> string_width.handle_grapheme_clusters, + string_width.new() |> string_width.mode_2027, ) // --> 2 } diff --git a/src/string_width.gleam b/src/string_width.gleam index aa246d3..833a405 100644 --- a/src/string_width.gleam +++ b/src/string_width.gleam @@ -15,29 +15,24 @@ import string_width/internal/tables // Mode 2027: https://github.com/contour-terminal/terminal-unicode-core // https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/36 -// v3: -// - implement line wrapping? truncation? pad/alignment? -// how much would this break optimisations? -// I should provide ways to implement layout, not do layout myself - // -- OPTIONS ------------------------------------------------------------------ /// Options to change the default behaviour of the functions in this library. /// If you are unsure what to do here, the defaults should work great! pub opaque type Options { Options( - count_ansi_escape_codes: Bool, + count_ansi_codes: Bool, ambiguous_width: Int, - handle_grapheme_clusters: Bool, + mode_2027: Bool, tab_width: Int, tab_offset: Int, ) } const default_options = Options( - count_ansi_escape_codes: False, + count_ansi_codes: False, ambiguous_width: 1, - handle_grapheme_clusters: False, + mode_2027: False, tab_width: 8, tab_offset: 0, ) @@ -47,6 +42,8 @@ pub fn new() -> Options { default_options } +/// Measure grapheme clusters instead of individual code points. +/// /// Most terminal emulators do not handle grapheme clusters well and will /// instead show their decomposition. To make sure a given string always fits /// even on those terminals, the functions in this package will copy this @@ -61,8 +58,8 @@ pub fn new() -> Options { /// /// See also [Grapheme Clusters and Terminal Emulators](https://mitchellh.com/writing/grapheme-clusters-in-terminals) /// for a better explanation on how terminals behave. -pub fn handle_grapheme_clusters(options: Options) -> Options { - Options(..options, handle_grapheme_clusters: True) +pub fn mode_2027(options: Options) -> Options { + Options(..options, mode_2027: True) } /// Some characters are marked by Unicode as "ambiguous", meaning they may @@ -79,8 +76,8 @@ pub fn ambiguous_as_wide(options: Options) -> Options { /// /// You can enable this option as an optimisation if you are sure that your /// string doesn't contain any ansi escape codes. -pub fn count_ansi_escape_codes(options: Options) -> Options { - Options(..options, count_ansi_escape_codes: True) +pub fn count_ansi_codes(options: Options) -> Options { + Options(..options, count_ansi_codes: True) } /// Change the number of columns between tab stops. (Default: 8) @@ -133,7 +130,7 @@ pub fn line(str: String) -> Int { /// ```gleam /// let options = /// new() -/// |> handle_grapheme_clusters +/// |> mode_2027 /// /// line_with("πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦", options) /// // --> 2 @@ -202,7 +199,7 @@ fn prepare_measure( options: Options, str: String, ) -> #(String, List(#(Int, Int)), Int) { - let ansi_ranges = case options.count_ansi_escape_codes { + let ansi_ranges = case options.count_ansi_codes { True -> [] False -> ansi.match(str) } @@ -212,7 +209,7 @@ fn prepare_measure( // -- FOLD --------------------------------------------------------------------- /// Iterate over the measured components of a string. Components are either -/// graphemes or codepoints, depending on the `handle_grapheme_clusters` option, +/// graphemes or codepoints, depending on the `mode_2027` option, /// or other undivisible sequences, like ANSI escape codes. /// /// This is a lower-level utility compared to the others in this package. It does @@ -248,7 +245,7 @@ pub fn fold( from state: state, with fun: fn(state, String, Int) -> state, ) -> state { - let ansi_ranges = case options.count_ansi_escape_codes { + let ansi_ranges = case options.count_ansi_codes { True -> [] False -> ansi.match(string) } @@ -295,7 +292,7 @@ fn do_fold_characters( state: state, fun: fn(state, String, Int) -> state, ) -> state { - case options.handle_grapheme_clusters { + case options.mode_2027 { True -> do_fold_graphemes(options, string, state, fun) False -> do_fold_codepoints(options, string, state, fun) } diff --git a/src/string_width_ffi.mjs b/src/string_width_ffi.mjs index 7f871be..0529c9e 100644 --- a/src/string_width_ffi.mjs +++ b/src/string_width_ffi.mjs @@ -5,7 +5,7 @@ const ASCII_RE = /[\x20-\x7e]+/g; const SEGMENTER = new Intl.Segmenter(); export function prepare_measure(options, str) { - if (!options.count_ansi_escape_codes) { + if (!options.count_ansi_codes) { str = str.replaceAll(ANSI_RE, '') } const ascii_ranges = scan_poslen(ASCII_RE, str) diff --git a/test/despair_test.gleam b/test/despair_test.gleam index be94b97..44d3140 100644 --- a/test/despair_test.gleam +++ b/test/despair_test.gleam @@ -4,7 +4,7 @@ import string_width fn sw(str: String) { let options = string_width.new() - |> string_width.handle_grapheme_clusters + |> string_width.mode_2027 string_width.line_with(str, options) } diff --git a/test/string_width_test.gleam b/test/string_width_test.gleam index 39ca88a..4ea9796 100644 --- a/test/string_width_test.gleam +++ b/test/string_width_test.gleam @@ -7,7 +7,7 @@ pub fn main() { } fn swg(str: String) { - let options = string_width.new() |> string_width.handle_grapheme_clusters + let options = string_width.new() |> string_width.mode_2027 string_width.line_with(str, options) } @@ -17,7 +17,7 @@ fn sww(str: String) { } fn swc(str: String) { - let options = string_width.new() |> string_width.count_ansi_escape_codes + let options = string_width.new() |> string_width.count_ansi_codes string_width.line_with(str, options) } @@ -46,6 +46,13 @@ pub fn docs_test() { dimensions("hello,\nμ•ˆλ…•ν•˜μ„Έμš”") |> should.equal(#(2, 10)) } +pub fn dimensions_test() { + dimensions("") |> should.equal(#(0, 0)) + dimensions("\n") |> should.equal(#(1, 0)) + dimensions("\nhello") |> should.equal(#(2, 5)) + dimensions("hello,\nμ•ˆλ…•ν•˜μ„Έμš”\n") |> should.equal(#(2, 10)) +} + pub fn pedantic_test() { // these are the test cases I had for v3. // I wanna document what other programs do, even if I think they are buggy. @@ -285,7 +292,7 @@ pub fn fold_identity_test() { } let default = string_width.new() - let clusters = default |> string_width.handle_grapheme_clusters + let clusters = default |> string_width.mode_2027 do_test("Àâüè", default) do_test("μ•ˆλ…•ν•˜μ„Έμš”", default)