File 2630-shell-Fix-latin1-handling-for-shell-prompt_width.patch of Package erlang

From 3257119108b574dbd7a61657a49d373a264b5831 Mon Sep 17 00:00:00 2001
From: Lukas Larsson <lukas@erlang.org>
Date: Fri, 5 Jan 2024 09:40:49 +0100
Subject: [PATCH 10/11] shell: Fix latin1 handling for shell:prompt_width

---
 lib/kernel/src/prim_tty.erl     |  7 +++++--
 lib/stdlib/doc/src/shell.xml    | 26 ++++++++++++++++++++++++++
 lib/stdlib/src/shell.erl        | 20 +++++++++++---------
 lib/stdlib/test/shell_SUITE.erl | 20 +++++++++++++++++---
 4 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/lib/kernel/src/prim_tty.erl b/lib/kernel/src/prim_tty.erl
index b8413cb090..67e873029b 100644
--- a/lib/kernel/src/prim_tty.erl
+++ b/lib/kernel/src/prim_tty.erl
@@ -105,7 +105,8 @@
 %%        to previous line automatically.
 
 -export([init/1, reinit/2, isatty/1, handles/1, unicode/1, unicode/2,
-         handle_signal/2, window_size/1, handle_request/2, write/2, write/3, npwcwidth/1,
+         handle_signal/2, window_size/1, handle_request/2, write/2, write/3,
+         npwcwidth/1, npwcwidth/2,
          ansi_regexp/0, ansi_color/2]).
 -export([reader_stop/1, disable_reader/1, enable_reader/1]).
 
@@ -1108,7 +1109,9 @@ npwcwidth(Char, true) ->
         C -> C
     end;
 npwcwidth(Char, false) ->
-    byte_size(char_to_latin1(Char, true)).
+    byte_size(char_to_latin1(Char, true));
+npwcwidth(Char, Encoding) ->
+    npwcwidth(Char, Encoding =/= latin1).
 
 
 %% Return the xn fix for the current cursor position.
diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml
index 996ef9c0be..600c7cb32f 100644
--- a/lib/stdlib/doc/src/shell.xml
+++ b/lib/stdlib/doc/src/shell.xml
@@ -1035,10 +1035,36 @@ q                 - quit erlang
     <func>
       <name name="prompt_width" arity="1" since="OTP 27.0"/>
       <fsummary>Computes the width of a prompt.</fsummary>
+      <desc>
+        <p>Equivalent to <seemfa marker="#prompt_width/2">prompt_width/2</seemfa> with
+        <c>Encoding</c> set to the encoding used by
+          <seetype marker="io#user"><c>io:user/0</c></seetype>.</p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="prompt_width" arity="2" since="OTP 27.0"/>
+      <fsummary>Computes the width of a prompt.</fsummary>
       <desc>
         <p>It receives a prompt and computes its width,
           considering its Unicode characters and ANSI escapes.</p>
         <p>Useful for creating custom multiline prompts.</p>
+        <p>Example:</p>
+        <code type="erl">
+1> shell:prompt_width("olá> ", unicode).
+5
+%% "olá> " is printed as "ol\341> " on a latin1 systems
+2> shell:prompt_width("olá> ", latin1).
+8
+%% Ansi escapes are ignored
+3> shell:prompt_width("\e[32molá\e[0m> ", unicode).
+5
+%% Double width characters count as 2
+4> shell:prompt_width("😀> ", unicode).
+4
+%% "😀> " is printed as "\x{1F600}> " on latin1 systems
+5> shell:prompt_width("😀> ", latin1).
+11</code>
       </desc>
     </func>
 
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 5164023290..a230ee48c6 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -28,7 +28,7 @@
 -export([start_interactive/0, start_interactive/1]).
 -export([read_and_add_records/5]).
 -export([default_multiline_prompt/1, inverted_space_prompt/1]).
--export([prompt_width/1]).
+-export([prompt_width/1, prompt_width/2]).
 -export([whereis/0]).
 
 -define(LINEMAX, 30).
@@ -1907,22 +1907,24 @@ strings(Strings) ->
     set_env(stdlib, shell_strings, Strings, ?DEF_STRINGS).
 
 -spec prompt_width(unicode:chardata()) -> non_neg_integer().
-
-prompt_width(String) when is_list(String) ->
-    prompt_width(unicode:characters_to_binary(String));
 prompt_width(String) ->
+    Encoding = proplists:get_value(encoding, io:getopts(user)),
+    prompt_width(String, Encoding).
+
+-spec prompt_width(unicode:chardata(), unicode | latin1) -> non_neg_integer().
+prompt_width(String, Encoding) ->
     case string:next_grapheme(String) of
         [] -> 0;
         [$\e | Rest] ->
             case re:run(String, prim_tty:ansi_regexp(), [unicode]) of
                 {match, [{0, N}]} ->
-                    <<_Ansi:N/binary, AnsiRest/binary>> = String,
-                    prompt_width(AnsiRest);
+                    <<_Ansi:N/binary, AnsiRest/binary>> = unicode:characters_to_binary(String),
+                    prompt_width(AnsiRest, Encoding);
                 _ ->
-                    prim_tty:npwcwidth($\e) + prompt_width(Rest)
+                    prim_tty:npwcwidth($\e, Encoding) + prompt_width(Rest, Encoding)
             end;
-        [H|Rest] when is_list(H)-> lists:sum([prim_tty:npwcwidth(A)||A<-H]) + prompt_width(Rest);
-        [H|Rest] -> prim_tty:npwcwidth(H) + prompt_width(Rest)
+        [H|Rest] when is_list(H)-> lists:sum([prim_tty:npwcwidth(A, Encoding)||A<-H]) + prompt_width(Rest, Encoding);
+        [H|Rest] -> prim_tty:npwcwidth(H, Encoding) + prompt_width(Rest, Encoding)
     end.
 
 -spec default_multiline_prompt(unicode:chardata()) ->
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 4913dbb060..55e8cca0ab 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -25,7 +25,7 @@
 	 bs_match_misc_SUITE/1, bs_match_int_SUITE/1,
 	 bs_match_tail_SUITE/1, bs_match_bin_SUITE/1,
 	 bs_construct_SUITE/1,
-     prompt_width/1,local_definitions_save_to_module_and_forget/1,
+         prompt_width/1,local_definitions_save_to_module_and_forget/1,
 	 refman_bit_syntax/1,
 	 progex_bit_syntax/1, progex_records/1,
 	 progex_lc/1, progex_funs/1,
@@ -425,8 +425,22 @@ shell_attribute_test(Config) ->
     ok.
 
 prompt_width(Config) when is_list(Config) ->
-    5 = shell:prompt_width("\e[31molá> "),
-    5 = shell:prompt_width(<<"\e[31molá> "/utf8>>),
+    5 = shell:prompt_width("olá> ", unicode),
+    5 = shell:prompt_width("\e[31molá> ", unicode),
+    5 = shell:prompt_width(<<"\e[31molá> "/utf8>>, unicode),
+    8 = shell:prompt_width("olá> ", latin1),
+    4 = shell:prompt_width("😀> ", unicode),
+    11 = shell:prompt_width("😀> ", latin1),
+    case proplists:get_value(encoding, io:getopts(user)) of
+        unicode ->
+            5 = shell:prompt_width("olá> "),
+            5 = shell:prompt_width("\e[31molá> "),
+            5 = shell:prompt_width(<<"\e[31molá> "/utf8>>),
+            4 = shell:prompt_width("😀> ");
+        latin1 ->
+            8 = shell:prompt_width("olá> "),
+            11 = shell:prompt_width("😀> ")
+    end,
     ok.
 
 %% Test of the record support. OTP-5063.
-- 
2.35.3

openSUSE Build Service is sponsored by