mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-05 15:15:42 +00:00
The ANSI escape sequence translation layer in TUI mode strips italic
or underlined text modes silently. You cannot output text formatted
like that using `TuiWindow.write` in Python at the moment.
Parse the ANSI escape sequences for italic and underlined text into
the `ui_file_style` structure and apply it to the TUI window when
applying styles, similar to preserving the bold/dim state already.
A script like this shows italic and underlined text correctly now.
```python
import gdb
class TestTUIWindow:
_tui_window: gdb.TuiWindow
def __init__(self, tui_window: gdb.TuiWindow) -> None:
self._tui_window = tui_window
self._tui_window.title = "colors test"
def render(self) -> None:
self._tui_window.write("""
\x1b[4mThis is underlined.\x1b[24m And normal text.
\x1b[3mThis is italic.\x1b[23m And normal text.
""", True)
gdb.register_window_type("colortest", TestTUIWindow)
```
And launching it with
```
source the_above_script.py
tui new-layout test colortest 1 cmd 1
layout test
```
Approved-By: Tom Tromey <tom@tromey.com>
248 lines
6.2 KiB
Plaintext
248 lines
6.2 KiB
Plaintext
# Copyright 2014-2025 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# Utility procedures, shared between test suite domains.
|
|
|
|
# A helper procedure to retrieve commands to send to GDB before a program
|
|
# is started.
|
|
|
|
proc gdb_init_commands {} {
|
|
set commands ""
|
|
if [target_info exists gdb_init_command] {
|
|
lappend commands [target_info gdb_init_command]
|
|
}
|
|
if [target_info exists gdb_init_commands] {
|
|
set commands [concat $commands [target_info gdb_init_commands]]
|
|
}
|
|
return $commands
|
|
}
|
|
|
|
# Given an input string, adds backslashes as needed to create a
|
|
# regexp that will match the string.
|
|
|
|
proc string_to_regexp {str} {
|
|
set result $str
|
|
regsub -all {[]?*+.|(){}^$\[\\]} $str {\\&} result
|
|
return $result
|
|
}
|
|
|
|
# Convenience function that calls string_to_regexp for each arg, and
|
|
# joins the results using "\r\n".
|
|
|
|
proc multi_line_string_to_regexp { args } {
|
|
set res [lmap arg $args {string_to_regexp $arg}]
|
|
return [multi_line {*}$res]
|
|
}
|
|
|
|
# Given a list of strings, adds backslashes as needed to each string to
|
|
# create a regexp that will match the string, and join the result.
|
|
|
|
proc string_list_to_regexp { args } {
|
|
set result ""
|
|
foreach arg $args {
|
|
set arg [string_to_regexp $arg]
|
|
append result $arg
|
|
}
|
|
return $result
|
|
}
|
|
|
|
# Wrap STR in an ANSI terminal escape sequences -- one to set the
|
|
# style to STYLE, and one to reset the style to the default. The
|
|
# return value is suitable for use as a regular expression.
|
|
|
|
# STYLE can either be the payload part of an ANSI terminal sequence,
|
|
# or a shorthand for one of the gdb standard styles: "file",
|
|
# "function", "variable", "address", etc.
|
|
|
|
proc style {str style} {
|
|
set fg 39
|
|
set bg 49
|
|
set intensity 22
|
|
set italic 23
|
|
set underline 24
|
|
set reverse 27
|
|
switch -exact -- $style {
|
|
title { set intensity 1 }
|
|
command { set intensity 1 }
|
|
file { set fg 32 }
|
|
function { set fg 33 }
|
|
highlight { set fg 31 }
|
|
variable { set fg 36 }
|
|
address { set fg 34 }
|
|
metadata { set intensity 2 }
|
|
version { set fg 35; set intensity 1 }
|
|
line-number { set intensity 2 }
|
|
none { return $str }
|
|
}
|
|
return "\033\\\[${fg};${bg};${intensity};${italic};${underline};${reverse}m${str}\033\\\[m"
|
|
}
|
|
|
|
# gdb_get_bp_addr num
|
|
#
|
|
# Purpose:
|
|
# Get address of a particular breakpoint.
|
|
#
|
|
# Parameter:
|
|
# The parameter "num" indicates the number of the breakpoint to get.
|
|
# Note that *currently* this parameter must be an integer value.
|
|
# E.g., -1 means that we're gonna get the first internal breakpoint;
|
|
# 2 means to get the second user-defined breakpoint.
|
|
#
|
|
# Return:
|
|
# First address for a particular breakpoint.
|
|
#
|
|
# TODO:
|
|
# It would be nice if this procedure could accept floating point value.
|
|
# E.g., 'gdb_get_bp_addr 1.2' means to get the address of the second
|
|
# location of breakpoint #1.
|
|
#
|
|
proc gdb_get_bp_addr { num } {
|
|
gdb_test_multiple "maint info break $num" "find address of specified bp $num" {
|
|
-re -wrap ".*(0x\[0-9a-f\]+).*" {
|
|
return $expect_out(1,string)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
# Compare the version numbers in L1 to those in L2 using OP, and
|
|
# return 1 if the comparison is true. OP can be "<", "<=", ">", ">=",
|
|
# or "==".
|
|
# It is ok if the lengths of the lists differ, but note that we have
|
|
# "{1} < {1 0}" instead of "{1} == {1 0}". See also
|
|
# gdb.testsuite/version-compare.exp.
|
|
|
|
proc version_compare { l1 op l2 } {
|
|
switch -exact $op {
|
|
"==" -
|
|
"<=" -
|
|
"<" {}
|
|
|
|
">=" {
|
|
# a >= b => b <= a
|
|
set x $l2
|
|
set l2 $l1
|
|
set l1 $x
|
|
set op "<="
|
|
}
|
|
|
|
">" {
|
|
# a > b => b < a
|
|
set x $l2
|
|
set l2 $l1
|
|
set l1 $x
|
|
set op "<"
|
|
}
|
|
|
|
default { error "unsupported op: $op" }
|
|
}
|
|
|
|
# Handle ops < and ==.
|
|
foreach v1 $l1 v2 $l2 {
|
|
if {$v1 == ""} {
|
|
# This is: "1.2 OP 1.2.1".
|
|
if {$op != "=="} {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
if {$v2 == ""} {
|
|
# This is: "1.2.1 OP 1.2".
|
|
return 0
|
|
}
|
|
if {$v1 == $v2} {
|
|
continue
|
|
}
|
|
return [expr $v1 $op $v2]
|
|
}
|
|
|
|
if {$op == "<"} {
|
|
# They are equal.
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
|
|
# Acquire lock file LOCKFILE. Tries forever until the lock file is
|
|
# successfully created.
|
|
|
|
proc lock_file_acquire {lockfile} {
|
|
verbose -log "acquiring lock file: $::subdir/${::gdb_test_file_name}.exp"
|
|
while {true} {
|
|
if {![catch {open $lockfile {WRONLY CREAT EXCL}} rc]} {
|
|
set msg "locked by $::subdir/${::gdb_test_file_name}.exp"
|
|
verbose -log "lock file: $msg"
|
|
# For debugging, put info in the lockfile about who owns
|
|
# it.
|
|
puts $rc $msg
|
|
flush $rc
|
|
return [list $rc $lockfile]
|
|
}
|
|
after 10
|
|
}
|
|
}
|
|
|
|
# Release a lock file.
|
|
|
|
proc lock_file_release {info} {
|
|
verbose -log "releasing lock file: $::subdir/${::gdb_test_file_name}.exp"
|
|
|
|
if {![catch {fconfigure [lindex $info 0]}]} {
|
|
if {![catch {
|
|
close [lindex $info 0]
|
|
file delete -force [lindex $info 1]
|
|
} rc]} {
|
|
return ""
|
|
} else {
|
|
return -code error "Error releasing lockfile: '$rc'"
|
|
}
|
|
} else {
|
|
error "invalid lock"
|
|
}
|
|
}
|
|
|
|
# Return directory where we keep lock files.
|
|
|
|
proc lock_dir {} {
|
|
if { [info exists ::GDB_LOCK_DIR] } {
|
|
# When using check//.
|
|
return $::GDB_LOCK_DIR
|
|
}
|
|
|
|
return [make_gdb_parallel_path cache]
|
|
}
|
|
|
|
# Run body under lock LOCK_FILE.
|
|
|
|
proc with_lock { lock_file body } {
|
|
if {[info exists ::GDB_PARALLEL]} {
|
|
set lock_file [file join [lock_dir] $lock_file]
|
|
set lock_rc [lock_file_acquire $lock_file]
|
|
}
|
|
|
|
set code [catch {uplevel 1 $body} result]
|
|
|
|
if {[info exists ::GDB_PARALLEL]} {
|
|
lock_file_release $lock_rc
|
|
}
|
|
|
|
if {$code == 1} {
|
|
global errorInfo errorCode
|
|
return -code $code -errorinfo $errorInfo -errorcode $errorCode $result
|
|
} else {
|
|
return -code $code $result
|
|
}
|
|
}
|