Skip to content

Commit 61da7e5

Browse files
authored
Merge pull request #597 from python-cmd2/display_width
Added wrapper for wcswidth that removes ansi escape characters
2 parents b6c3936 + bced087 commit 61da7e5

File tree

3 files changed

+21
-6
lines changed

3 files changed

+21
-6
lines changed

cmd2/cmd2.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343

4444
import colorama
4545
from colorama import Fore
46-
from wcwidth import wcswidth
4746

4847
from . import constants
4948
from . import plugin
@@ -1302,7 +1301,7 @@ def _display_matches_gnu_readline(self, substitution: str, matches: List[str],
13021301
longest_match_length = 0
13031302

13041303
for cur_match in matches_to_display:
1305-
cur_length = wcswidth(cur_match)
1304+
cur_length = utils.display_width(cur_match)
13061305
if cur_length > longest_match_length:
13071306
longest_match_length = cur_length
13081307
else:
@@ -2662,7 +2661,7 @@ def _print_topics(self, header: str, cmds: List[str], verbose: bool) -> None:
26622661
widest = 0
26632662
# measure the commands
26642663
for command in cmds:
2665-
width = len(command)
2664+
width = utils.display_width(command)
26662665
if width > widest:
26672666
widest = width
26682667
# add a 4-space pad
@@ -3478,14 +3477,14 @@ def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:
34783477
update_terminal = True
34793478

34803479
if update_terminal:
3481-
# Remove ansi characters to get the visible width of the prompt
3482-
prompt_width = wcswidth(utils.strip_ansi(current_prompt))
3480+
# Get the display width of the prompt
3481+
prompt_width = utils.display_width(current_prompt)
34833482

34843483
# Get the size of the terminal
34853484
terminal_size = shutil.get_terminal_size()
34863485

34873486
# Figure out how many lines the prompt and user input take up
3488-
total_str_size = prompt_width + wcswidth(readline.get_line_buffer())
3487+
total_str_size = prompt_width + utils.display_width(readline.get_line_buffer())
34893488
num_input_lines = int(total_str_size / terminal_size.columns) + 1
34903489

34913490
# Get the cursor's offset from the beginning of the first input line

cmd2/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import unicodedata
99
from typing import Any, Iterable, List, Optional, Union
1010

11+
from wcwidth import wcswidth
12+
1113
from . import constants
1214

1315

@@ -20,6 +22,15 @@ def strip_ansi(text: str) -> str:
2022
return constants.ANSI_ESCAPE_RE.sub('', text)
2123

2224

25+
def display_width(text: str) -> int:
26+
"""
27+
Return the printable length of a string. This can be different than character count in unicode strings.
28+
:param text: the string being measured
29+
"""
30+
# Strip ANSI escape codes since they cause wcswidth to return -1
31+
return wcswidth(strip_ansi(text))
32+
33+
2334
def is_quoted(arg: str) -> bool:
2435
"""
2536
Checks if a string is quoted

tests/test_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ def test_strip_ansi():
2121
assert base_str != ansi_str
2222
assert base_str == cu.strip_ansi(ansi_str)
2323

24+
def test_display_width():
25+
base_str = HELLO_WORLD
26+
ansi_str = Fore.GREEN + base_str + Fore.RESET
27+
assert cu.display_width(ansi_str) != len(ansi_str)
28+
2429
def test_strip_quotes_no_quotes():
2530
base_str = HELLO_WORLD
2631
stripped = cu.strip_quotes(base_str)

0 commit comments

Comments
 (0)