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