Make wcswidth ignore more escape codes
Needed to ignore OSC 8 hyperlinks since we will eventually have to send those with SGR formatting to the hints kitten.
This commit is contained in:
@@ -2061,13 +2061,17 @@ screen_wcswidth(PyObject UNUSED *self, PyObject *str) {
|
|||||||
unsigned long ans = 0;
|
unsigned long ans = 0;
|
||||||
char_type prev_ch = 0;
|
char_type prev_ch = 0;
|
||||||
int prev_width = 0;
|
int prev_width = 0;
|
||||||
typedef enum {NORMAL, IN_SGR, FLAG_PAIR_STARTED} WCSState;
|
typedef enum {NORMAL, IN_CSI, FLAG_PAIR_STARTED, IN_ST_TERMINATED} WCSState;
|
||||||
WCSState state = NORMAL;
|
WCSState state = NORMAL;
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
char_type ch = PyUnicode_READ(kind, data, i);
|
char_type ch = PyUnicode_READ(kind, data, i);
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case IN_SGR: {
|
case IN_CSI: {
|
||||||
if (ch == 'm') state = NORMAL;
|
if (0x40 <= ch && ch <= 0x7e) state = NORMAL;
|
||||||
|
} continue;
|
||||||
|
case IN_ST_TERMINATED: {
|
||||||
|
if (ch == 0x9c) state = NORMAL;
|
||||||
|
else if (ch == 0x1b && i + 1 < len && PyUnicode_READ(kind, data, i + 1) == '\\') { i++; state = NORMAL; }
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
case FLAG_PAIR_STARTED: {
|
case FLAG_PAIR_STARTED: {
|
||||||
@@ -2076,8 +2080,46 @@ screen_wcswidth(PyObject UNUSED *self, PyObject *str) {
|
|||||||
} /* fallthrough */
|
} /* fallthrough */
|
||||||
|
|
||||||
case NORMAL: {
|
case NORMAL: {
|
||||||
if (ch == 0x1b && i + 1 < len && PyUnicode_READ(kind, data, i + 1) == '[') { state = IN_SGR; continue; }
|
|
||||||
switch(ch) {
|
switch(ch) {
|
||||||
|
case 0x1b: {
|
||||||
|
prev_width = 0;
|
||||||
|
if (i + 1 < len) {
|
||||||
|
switch (PyUnicode_READ(kind, data, i + 1)) {
|
||||||
|
case '[':
|
||||||
|
state = IN_CSI; i++; continue;
|
||||||
|
case 'P':
|
||||||
|
case ']':
|
||||||
|
case 'X':
|
||||||
|
case '^':
|
||||||
|
case '_':
|
||||||
|
state = IN_ST_TERMINATED; i++; continue;
|
||||||
|
case 'D':
|
||||||
|
case 'E':
|
||||||
|
case 'H':
|
||||||
|
case 'M':
|
||||||
|
case 'N':
|
||||||
|
case 'O':
|
||||||
|
case 'Z':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
case '=':
|
||||||
|
case '>':
|
||||||
|
case 'F':
|
||||||
|
case 'c':
|
||||||
|
case 'l':
|
||||||
|
case 'm':
|
||||||
|
case 'n':
|
||||||
|
case 'o':
|
||||||
|
case '|':
|
||||||
|
case '}':
|
||||||
|
case '~':
|
||||||
|
i++; continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case 0xfe0f: {
|
case 0xfe0f: {
|
||||||
if (is_emoji_presentation_base(prev_ch) && prev_width == 1) {
|
if (is_emoji_presentation_base(prev_ch) && prev_width == 1) {
|
||||||
ans += 1;
|
ans += 1;
|
||||||
|
|||||||
@@ -354,9 +354,11 @@ class TestDataTypes(BaseTest):
|
|||||||
def test_utils(self):
|
def test_utils(self):
|
||||||
def w(x):
|
def w(x):
|
||||||
return wcwidth(ord(x))
|
return wcwidth(ord(x))
|
||||||
|
self.ae(wcswidth('a\033[2mb'), 2)
|
||||||
|
self.ae(wcswidth('\033a\033[2mb'), 2)
|
||||||
|
self.ae(wcswidth('a\033]8;id=moo;https://foo\033\\a'), 2)
|
||||||
self.ae(tuple(map(w, 'a1\0コニチ ✔')), (1, 1, 0, 2, 2, 2, 1, 1))
|
self.ae(tuple(map(w, 'a1\0コニチ ✔')), (1, 1, 0, 2, 2, 2, 1, 1))
|
||||||
self.ae(wcswidth('\u2716\u2716\ufe0f\U0001f337'), 5)
|
self.ae(wcswidth('\u2716\u2716\ufe0f\U0001f337'), 5)
|
||||||
self.ae(wcswidth('\033a\033[2mb'), 2)
|
|
||||||
self.ae(wcswidth('\u25b6\ufe0f'), 2)
|
self.ae(wcswidth('\u25b6\ufe0f'), 2)
|
||||||
self.ae(wcswidth('\U0001f610\ufe0e'), 1)
|
self.ae(wcswidth('\U0001f610\ufe0e'), 1)
|
||||||
self.ae(wcswidth('\U0001f1e6a'), 3)
|
self.ae(wcswidth('\U0001f1e6a'), 3)
|
||||||
|
|||||||
Reference in New Issue
Block a user