mirror of
https://github.com/XuehaiPan/nvitop.git
synced 2026-05-15 06:06:12 -06:00
feat(tui): implement curses emulation with ANSI escape sequences
- Add complete terminal handling with screen buffer management - Implement keyboard and mouse input (SGR extended mouse protocol) - Add support for text attributes, colors, and ACS line-drawing characters - Handle wide characters and combining character merging - Support both Windows (msvcrt) and Unix (termios) platforms - Fix ERR constant to match standard curses (-1 instead of 1) - Fix unctrl() return type annotation (str instead of int)
This commit is contained in:
parent
319e88f16b
commit
a8fe6924ad
5 changed files with 1760 additions and 292 deletions
|
|
@ -1,20 +1,30 @@
|
|||
accessdenied
|
||||
acs
|
||||
addch
|
||||
addnstr
|
||||
addstr
|
||||
api
|
||||
args
|
||||
ascii
|
||||
attr
|
||||
bel
|
||||
bg
|
||||
bool
|
||||
boolean
|
||||
bstate
|
||||
cbreak
|
||||
chgat
|
||||
chtype
|
||||
cli
|
||||
cmdline
|
||||
codepoint
|
||||
colorama
|
||||
colorscheme
|
||||
compat
|
||||
conf
|
||||
const
|
||||
cpython
|
||||
csi
|
||||
csv
|
||||
ctrl
|
||||
ctx
|
||||
|
|
@ -29,6 +39,7 @@ divmod
|
|||
docstring
|
||||
doctest
|
||||
ecc
|
||||
endwin
|
||||
enum
|
||||
env
|
||||
environ
|
||||
|
|
@ -36,6 +47,7 @@ esc
|
|||
failsafe
|
||||
fallbacks
|
||||
fg
|
||||
fileno
|
||||
fmt
|
||||
func
|
||||
getch
|
||||
|
|
@ -61,6 +73,7 @@ keras
|
|||
kib
|
||||
kmd
|
||||
kwargs
|
||||
leaveok
|
||||
len
|
||||
libcuda
|
||||
libcudart
|
||||
|
|
@ -75,6 +88,7 @@ mig
|
|||
migdevice
|
||||
milliwatts
|
||||
mps
|
||||
msvcrt
|
||||
mypy
|
||||
namespace
|
||||
nan
|
||||
|
|
@ -82,6 +96,7 @@ noheader
|
|||
noqa
|
||||
nosuchprocess
|
||||
nounits
|
||||
num
|
||||
nvidia
|
||||
nvidia-smi
|
||||
nvisel
|
||||
|
|
@ -90,6 +105,7 @@ nvml
|
|||
nvmlerror
|
||||
oneshot
|
||||
ord
|
||||
ored
|
||||
os
|
||||
ot
|
||||
pan
|
||||
|
|
@ -107,18 +123,27 @@ pytorch
|
|||
redhat
|
||||
reentrant
|
||||
resourcemetriccollector
|
||||
rgb
|
||||
rlist
|
||||
rss
|
||||
rstrip
|
||||
rtx
|
||||
runtime
|
||||
rw
|
||||
rx
|
||||
rxvt
|
||||
sanitization
|
||||
scrollok
|
||||
selectable
|
||||
setupterm
|
||||
sgi's
|
||||
sgr
|
||||
shader
|
||||
sm
|
||||
smi
|
||||
stderr
|
||||
stdin
|
||||
stdout
|
||||
stdscr
|
||||
str
|
||||
struct
|
||||
|
|
@ -130,6 +155,7 @@ superset
|
|||
sys
|
||||
tcc
|
||||
tensorflow
|
||||
termios
|
||||
throughputinfo
|
||||
toml
|
||||
traceback
|
||||
|
|
@ -141,10 +167,13 @@ uid
|
|||
uids
|
||||
unallocated
|
||||
uncase
|
||||
ungetch
|
||||
unicode
|
||||
unicodedata
|
||||
uptime
|
||||
utils
|
||||
uuid
|
||||
wcwidth
|
||||
wddm
|
||||
wdm
|
||||
widestring
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ else:
|
|||
if not HAS_CURSES_MODULE:
|
||||
# pylint: disable-next=redefined-builtin
|
||||
from nvitop.tui.library.curses import ascii # type: ignore[no-redef]
|
||||
from nvitop.tui.library.curses._curses import * # type: ignore[assignment,no-redef] # noqa: F403
|
||||
from nvitop.tui.library.curses._curses import * # type: ignore[assignment] # noqa: F403
|
||||
|
||||
if _TYPE_CHECKING:
|
||||
from collections.abc import Callable as _Callable
|
||||
|
|
@ -77,7 +77,7 @@ if not HAS_CURSES_MODULE:
|
|||
def start_color() -> None: # pylint: disable=function-redefined
|
||||
from nvitop.tui.library.curses import _curses
|
||||
|
||||
retval = _curses.start_color() # type: ignore[func-returns-value]
|
||||
retval = _curses.start_color() # type: ignore[func-returns-value] # pylint: disable=assignment-from-no-return
|
||||
if hasattr(_curses, 'COLORS'):
|
||||
globals()['COLORS'] = _curses.COLORS
|
||||
if hasattr(_curses, 'COLOR_PAIRS'):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -193,14 +193,14 @@ def alt(c: int | str) -> int | str:
|
|||
|
||||
|
||||
@overload
|
||||
def unctrl(c: int) -> int: ...
|
||||
def unctrl(c: int) -> str: ...
|
||||
|
||||
|
||||
@overload
|
||||
def unctrl(c: str) -> str: ...
|
||||
|
||||
|
||||
def unctrl(c: int | str) -> int | str:
|
||||
def unctrl(c: int | str) -> str:
|
||||
bits = _ctoi(c)
|
||||
if bits == 0x7F:
|
||||
rep = '^?'
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import unicodedata
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
from unicodedata import east_asian_width
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -18,27 +18,50 @@ __all__ = ['WideString', 'wcslen']
|
|||
|
||||
|
||||
ASCIIONLY: frozenset[str] = frozenset(map(chr, range(1, 128)))
|
||||
COMBINING: Literal[0] = 0
|
||||
NARROW: Literal[1] = 1
|
||||
WIDE: Literal[2] = 2
|
||||
WIDE_SYMBOLS: frozenset[str] = frozenset('WF')
|
||||
|
||||
|
||||
def utf_char_width(string: str) -> Literal[1, 2]:
|
||||
"""Return the width of a single character."""
|
||||
if east_asian_width(string) in WIDE_SYMBOLS:
|
||||
return WIDE
|
||||
def utf_char_width(string: str) -> Literal[0, 1, 2]: # pylint: disable=too-many-return-statements
|
||||
"""Return the width of a single character (0 for combining, 2 for wide, 1 otherwise)."""
|
||||
try:
|
||||
import wcwidth # pylint: disable=import-outside-toplevel
|
||||
|
||||
w = wcwidth.wcwidth(string)
|
||||
if w < 0:
|
||||
return NARROW # control characters treated as width 1
|
||||
if w == 0:
|
||||
return COMBINING
|
||||
if w >= 2:
|
||||
return WIDE
|
||||
except ImportError:
|
||||
# Fallback to unicodedata
|
||||
if unicodedata.combining(string):
|
||||
return COMBINING
|
||||
if unicodedata.east_asian_width(string) in WIDE_SYMBOLS:
|
||||
return WIDE
|
||||
return NARROW
|
||||
|
||||
|
||||
def string_to_charlist(string: str) -> list[str]:
|
||||
"""Return a list of characters with extra empty strings after wide chars."""
|
||||
"""Return a list of characters with extra empty strings after wide chars.
|
||||
|
||||
Combining characters (width 0) are merged with the preceding character.
|
||||
"""
|
||||
if ASCIIONLY.issuperset(string):
|
||||
return list(string)
|
||||
result = []
|
||||
result: list[str] = []
|
||||
for char in string:
|
||||
result.append(char)
|
||||
if east_asian_width(char) in WIDE_SYMBOLS:
|
||||
result.append('')
|
||||
width = utf_char_width(char)
|
||||
if width == COMBINING and result:
|
||||
# Merge combining character with the previous character
|
||||
result[-1] += char
|
||||
else:
|
||||
result.append(char)
|
||||
if width == WIDE:
|
||||
result.append('')
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue