@@ -1425,24 +1425,49 @@ def test_no_newline(self):
14251425 output , exit_code = self .run_repl (commands )
14261426 self .assertEqual (exit_code , 0 )
14271427
1428- # Define escape sequences that don't affect cursor position or visual output
1429- bracketed_paste_mode = r'\x1b\[\?2004[hl]' # Enable/disable bracketed paste
1430- application_cursor_keys = r'\x1b\[\?1[hl]' # Enable/disable application cursor keys
1431- application_keypad_mode = r'\x1b[=>]' # Enable/disable application keypad
1432- insert_character = r'\x1b\[(?:1)?@(?=[ -~])' # Insert exactly 1 char (safe form)
1433- cursor_visibility = r'\x1b\[\?25[hl]' # Show/hide cursor
1434- cursor_blinking = r'\x1b\[\?12[hl]' # Start/stop cursor blinking
1435- device_attributes = r'\x1b\[\?[01]c' # Device Attributes (DA) queries/responses
1436-
1437- safe_escapes = re .compile (
1438- f'{ bracketed_paste_mode } |'
1439- f'{ application_cursor_keys } |'
1440- f'{ application_keypad_mode } |'
1441- f'{ insert_character } |'
1442- f'{ cursor_visibility } |'
1443- f'{ cursor_blinking } |'
1444- f'{ device_attributes } '
1445- )
1428+ # Build patterns for escape sequences that don't affect cursor position
1429+ # or visual output. Use terminfo to get platform-specific sequences,
1430+ # falling back to hard-coded patterns for capabilities not in terminfo.
1431+ from _pyrepl .terminfo import TermInfo
1432+ ti = TermInfo (os .environ .get ("TERM" , "" ))
1433+
1434+ safe_patterns = []
1435+
1436+ # smkx/rmkx - application cursor keys and keypad mode
1437+ smkx = ti .get ("smkx" )
1438+ rmkx = ti .get ("rmkx" )
1439+ if smkx :
1440+ safe_patterns .append (re .escape (smkx .decode ("ascii" )))
1441+ if rmkx :
1442+ safe_patterns .append (re .escape (rmkx .decode ("ascii" )))
1443+ if not smkx and not rmkx :
1444+ safe_patterns .append (r'\x1b\[\?1[hl]' ) # application cursor keys
1445+ safe_patterns .append (r'\x1b[=>]' ) # application keypad mode
1446+
1447+ # ich1 - insert character (only safe form that inserts exactly 1 char)
1448+ ich1 = ti .get ("ich1" )
1449+ if ich1 :
1450+ safe_patterns .append (re .escape (ich1 .decode ("ascii" )) + r'(?=[ -~])' )
1451+ else :
1452+ safe_patterns .append (r'\x1b\[(?:1)?@(?=[ -~])' )
1453+
1454+ # civis/cnorm - cursor visibility (may include cursor blinking control)
1455+ civis = ti .get ("civis" )
1456+ cnorm = ti .get ("cnorm" )
1457+ if civis :
1458+ safe_patterns .append (re .escape (civis .decode ("ascii" )))
1459+ if cnorm :
1460+ safe_patterns .append (re .escape (cnorm .decode ("ascii" )))
1461+ if not civis and not cnorm :
1462+ safe_patterns .append (r'\x1b\[\?25[hl]' ) # cursor visibility
1463+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking
1464+
1465+ # Modern extensions not in standard terminfo - always use patterns
1466+ safe_patterns .append (r'\x1b\[\?2004[hl]' ) # bracketed paste mode
1467+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking (may be separate)
1468+ safe_patterns .append (r'\x1b\[\?[01]c' ) # device attributes
1469+
1470+ safe_escapes = re .compile ('|' .join (safe_patterns ))
14461471 cleaned_output = safe_escapes .sub ('' , output )
14471472 self .assertIn (expected_output_sequence , cleaned_output )
14481473
0 commit comments