Skip to content

Commit f06c493

Browse files
committed
[lldb][Format] Add new function basename highlight option to FormatEntity
1 parent 8f707f2 commit f06c493

File tree

9 files changed

+276
-24
lines changed

9 files changed

+276
-24
lines changed

lldb/include/lldb/Core/FormatEntity.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,41 @@ struct Entry {
197197
return true;
198198
}
199199

200+
/// Describes how a function name should be highlighted.
201+
struct HighlightSettings {
202+
/// ANSI prefix that will be printed before the function name.
203+
std::string prefix;
204+
205+
/// ANSI suffix that will be printed after the function name.
206+
std::string suffix;
207+
208+
/// What kind of highlighting the user asked to perform.
209+
enum class Kind : uint8_t {
210+
///< Don't highlight.
211+
None,
212+
213+
///< Highlight function basename
214+
///< (i.e., name without Scope and
215+
///< without template arguments).
216+
Basename,
217+
} kind = Kind::None;
218+
};
219+
200220
std::string string;
201221
std::string printf_format;
202222
std::vector<Entry> children;
203223
Type type;
204224
lldb::Format fmt = lldb::eFormatDefault;
205225
lldb::addr_t number = 0;
206226
bool deref = false;
227+
228+
/// Set using the highlighting format specifiers to a
229+
/// frame-format variable.
230+
/// E.g.,
231+
/// \code
232+
/// ${function.name-with-args:%highlight_basename(ansi.fg.green)}
233+
/// \endcode
234+
HighlightSettings highlight;
207235
};
208236

209237
bool Format(const Entry &entry, Stream &s, const SymbolContext *sc,

lldb/include/lldb/Symbol/Function.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "lldb/Core/AddressRange.h"
1313
#include "lldb/Core/Declaration.h"
14+
#include "lldb/Core/DemangledNameInfo.h"
1415
#include "lldb/Core/Mangled.h"
1516
#include "lldb/Expression/DWARFExpressionList.h"
1617
#include "lldb/Symbol/Block.h"
@@ -534,6 +535,9 @@ class Function : public UserID, public SymbolContextScope {
534535

535536
ConstString GetDisplayName() const;
536537

538+
/// Retrieve \c DemangledNameInfo of the demangled name held by this object.
539+
const std::optional<DemangledNameInfo> &GetDemangledInfo() const;
540+
537541
const Mangled &GetMangled() const { return m_mangled; }
538542

539543
/// Get the DeclContext for this function, if available.

lldb/include/lldb/Target/Language.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,11 @@ class Language : public PluginInterface {
268268
// the reference has never been assigned
269269
virtual bool IsUninitializedReference(ValueObject &valobj);
270270

271-
virtual bool GetFunctionDisplayName(const SymbolContext *sc,
272-
const ExecutionContext *exe_ctx,
273-
FunctionNameRepresentation representation,
274-
Stream &s);
271+
virtual bool
272+
GetFunctionDisplayName(const SymbolContext *sc,
273+
const ExecutionContext *exe_ctx,
274+
FunctionNameRepresentation representation, Stream &s,
275+
const FormatEntity::Entry::HighlightSettings &);
275276

276277
virtual ConstString
277278
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const {

lldb/source/Core/FormatEntity.cpp

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16541654

16551655
if (language_plugin)
16561656
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1657-
sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1657+
sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss,
1658+
entry.highlight);
16581659

16591660
if (language_plugin_handled) {
16601661
s << ss.GetString();
@@ -1690,7 +1691,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16901691
if (language_plugin)
16911692
language_plugin_handled = language_plugin->GetFunctionDisplayName(
16921693
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1693-
ss);
1694+
ss, entry.highlight);
16941695

16951696
if (language_plugin_handled) {
16961697
s << ss.GetString();
@@ -1724,7 +1725,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
17241725

17251726
if (language_plugin)
17261727
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1727-
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
1728+
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss,
1729+
entry.highlight);
17281730

17291731
if (language_plugin_handled) {
17301732
s << ss.GetString();
@@ -2046,6 +2048,64 @@ static const Definition *FindEntry(const llvm::StringRef &format_str,
20462048
return parent;
20472049
}
20482050

2051+
/// Parses a single highlighting format specifier.
2052+
///
2053+
/// Example syntax for such specifier:
2054+
/// \code
2055+
/// ${function.name-with-args:%highlight_basename(ansi.fg.green)}
2056+
/// \endcode
2057+
///
2058+
/// In the above snippet, the `function.name-with-args` frame-format
2059+
/// variable will have its basename highlighted in green.
2060+
static llvm::Expected<Entry::HighlightSettings>
2061+
ParseHighlightSettings(const Entry &entry) {
2062+
// FIXME: support other function.name-XXX types as well
2063+
if (entry.type != Entry::Type::FunctionNameWithArgs)
2064+
return llvm::createStringError(
2065+
"The 'highlight_basename' format can only be used on "
2066+
"${function.name-with-args}");
2067+
2068+
llvm::StringRef format = entry.printf_format;
2069+
if (!format.consume_front("highlight_"))
2070+
return llvm::createStringError(
2071+
"Expected 'highlight_' prefix not found in: %s.",
2072+
entry.printf_format.c_str());
2073+
2074+
Entry::HighlightSettings settings;
2075+
if (format.consume_front("basename")) {
2076+
settings.kind = Entry::HighlightSettings::Kind::Basename;
2077+
} else {
2078+
return llvm::createStringError(
2079+
"Unsupported highlight kind detected in: %s. "
2080+
"Currently supported: basename",
2081+
entry.printf_format.c_str());
2082+
}
2083+
2084+
llvm::SmallVector<llvm::StringRef, 1> matches;
2085+
// TODO: support ${ansi.XXX} syntax. ExtractVariableInfo needs
2086+
// to be adjusted to support nested '{}'.
2087+
llvm::Regex color_pattern{R"(^\(([a-z\.]+)\)$)"};
2088+
if (!color_pattern.match(format, &matches))
2089+
return llvm::createStringError(
2090+
"Couldn't find valid color variable in: %s. "
2091+
"Expected format: %%highlight_basename(ansi.some-color)",
2092+
entry.printf_format.c_str());
2093+
2094+
assert(matches.size() == 2);
2095+
2096+
std::string color_format = ("${" + matches[1] + "}").str();
2097+
std::string terminal_code = ansi::FormatAnsiTerminalCodes(color_format);
2098+
if (terminal_code.empty() || terminal_code == color_format)
2099+
return llvm::createStringError("Invalid color variable '%s' found in: %s",
2100+
color_format.c_str(),
2101+
entry.printf_format.c_str());
2102+
2103+
settings.prefix = std::move(terminal_code);
2104+
settings.suffix = ansi::FormatAnsiTerminalCodes("${ansi.normal}");
2105+
2106+
return settings;
2107+
}
2108+
20492109
static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
20502110
uint32_t depth) {
20512111
Status error;
@@ -2201,6 +2261,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
22012261
if (error.Fail())
22022262
return error;
22032263
bool verify_is_thread_id = false;
2264+
bool parse_highlight_settings = false;
22042265
Entry entry;
22052266
if (!variable_format.empty()) {
22062267
entry.printf_format = variable_format.str();
@@ -2266,6 +2327,8 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
22662327
clear_printf = true;
22672328
} else if (entry.printf_format == "tid") {
22682329
verify_is_thread_id = true;
2330+
} else if (entry.printf_format.find("highlight_") == 0) {
2331+
parse_highlight_settings = true;
22692332
} else {
22702333
error = Status::FromErrorStringWithFormat(
22712334
"invalid format: '%s'", entry.printf_format.c_str());
@@ -2307,6 +2370,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
23072370
"the 'tid' format can only be used on "
23082371
"${thread.id} and ${thread.protocol_id}");
23092372
}
2373+
} else if (parse_highlight_settings) {
2374+
auto highlight_or_err = ParseHighlightSettings(entry);
2375+
if (highlight_or_err) {
2376+
entry.highlight = std::move(*highlight_or_err);
2377+
entry.printf_format.clear();
2378+
} else {
2379+
error = Status::FromError(highlight_or_err.takeError());
2380+
}
23102381
}
23112382

23122383
switch (entry.type) {

lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include "llvm/ADT/StringRef.h"
2020
#include "llvm/Demangle/ItaniumDemangle.h"
2121

22+
#include "lldb/Core/Debugger.h"
23+
#include "lldb/Core/DemangledNameInfo.h"
24+
#include "lldb/Core/FormatEntity.h"
2225
#include "lldb/Core/Mangled.h"
2326
#include "lldb/Core/Module.h"
2427
#include "lldb/Core/PluginManager.h"
@@ -178,7 +181,7 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) {
178181
/// but replaces each argument type with the variable name
179182
/// and the corresponding pretty-printed value
180183
static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
181-
char const *full_name,
184+
llvm::StringRef full_name,
182185
ExecutionContextScope *exe_scope,
183186
VariableList const &args) {
184187
CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)};
@@ -208,6 +211,71 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
208211
return true;
209212
}
210213

214+
static bool GetUseColor(ExecutionContextScope *exe_scope) {
215+
if (!exe_scope)
216+
return false;
217+
218+
auto target_sp = exe_scope->CalculateTarget();
219+
if (!target_sp)
220+
return false;
221+
222+
return target_sp->GetDebugger().GetUseColor();
223+
}
224+
225+
static bool ShouldHighlightBasename(
226+
ExecutionContextScope *exe_scope,
227+
const FormatEntity::Entry::HighlightSettings &settings) {
228+
if (!GetUseColor(exe_scope))
229+
return false;
230+
231+
return settings.kind ==
232+
FormatEntity::Entry::HighlightSettings::Kind::Basename;
233+
}
234+
235+
/// If \c DemangledNameInfo is valid, we use it to print a function function
236+
/// name into \c out_stream (and if requested, highlight the basename).
237+
static bool PrettyPrintHighlightedBasenameWithArgs(
238+
Stream &out_stream, llvm::StringRef full_name,
239+
ExecutionContextScope *exe_scope, VariableList const &args,
240+
const std::optional<DemangledNameInfo> &demangled_info,
241+
const FormatEntity::Entry::HighlightSettings &settings) {
242+
// If we didn't get sufficient information from the demangler, we fall back to
243+
// parsing the and printing parts of the demangled name ourselves.
244+
if (!demangled_info || !demangled_info->hasBasename())
245+
return PrettyPrintFunctionNameWithArgs(out_stream, full_name, exe_scope,
246+
args);
247+
248+
auto [base_start, base_end] = demangled_info->BasenameRange;
249+
250+
// Dump anything before the basename.
251+
out_stream.PutCString(full_name.substr(0, base_start));
252+
253+
// Highlight the basename.
254+
if (ShouldHighlightBasename(exe_scope, settings))
255+
out_stream.PutCString(settings.prefix);
256+
257+
out_stream.PutCString(full_name.substr(base_start, base_end - base_start));
258+
259+
if (ShouldHighlightBasename(exe_scope, settings))
260+
out_stream.PutCString(settings.suffix);
261+
262+
// Dump anything between the basename and the argument list.
263+
if (demangled_info->ArgumentsRange.first > base_end)
264+
out_stream.PutCString(full_name.substr(
265+
base_end, demangled_info->ArgumentsRange.first - base_end));
266+
267+
// Dump arguments.
268+
out_stream.PutChar('(');
269+
FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope);
270+
out_stream.PutChar(')');
271+
272+
// Dump anything after the argument list.
273+
out_stream.PutCString(
274+
full_name.substr(demangled_info->ArgumentsRange.second));
275+
276+
return true;
277+
}
278+
211279
bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() {
212280
// This method tries to parse simple method definitions which are presumably
213281
// most comman in user programs. Definitions that can be parsed by this
@@ -1699,7 +1767,8 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
16991767

17001768
bool CPlusPlusLanguage::GetFunctionDisplayName(
17011769
const SymbolContext *sc, const ExecutionContext *exe_ctx,
1702-
FunctionNameRepresentation representation, Stream &s) {
1770+
FunctionNameRepresentation representation, Stream &s,
1771+
const FormatEntity::Entry::HighlightSettings &settings) {
17031772
switch (representation) {
17041773
case FunctionNameRepresentation::eNameWithArgs: {
17051774
// Print the function name with arguments in it
@@ -1737,13 +1806,10 @@ bool CPlusPlusLanguage::GetFunctionDisplayName(
17371806
if (variable_list_sp)
17381807
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
17391808
args);
1740-
if (args.GetSize() > 0) {
1741-
if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args))
1742-
return false;
1743-
} else {
1744-
s.PutCString(cstr);
1745-
}
1746-
return true;
1809+
1810+
return PrettyPrintHighlightedBasenameWithArgs(
1811+
s, cstr, exe_scope, args, sc->function->GetDemangledInfo(),
1812+
settings);
17471813
}
17481814
} else if (sc->symbol) {
17491815
const char *cstr = sc->symbol->GetName().AsCString(nullptr);

lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/StringRef.h"
1616

1717
#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
18+
#include "lldb/Core/FormatEntity.h"
1819
#include "lldb/Target/Language.h"
1920
#include "lldb/Utility/ConstString.h"
2021
#include "lldb/lldb-private.h"
@@ -138,10 +139,10 @@ class CPlusPlusLanguage : public Language {
138139
ConstString
139140
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override;
140141

141-
bool GetFunctionDisplayName(const SymbolContext *sc,
142-
const ExecutionContext *exe_ctx,
143-
FunctionNameRepresentation representation,
144-
Stream &s) override;
142+
bool GetFunctionDisplayName(
143+
const SymbolContext *sc, const ExecutionContext *exe_ctx,
144+
FunctionNameRepresentation representation, Stream &s,
145+
const FormatEntity::Entry::HighlightSettings &) override;
145146

146147
static bool IsCPPMangledName(llvm::StringRef name);
147148

lldb/source/Symbol/Function.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,3 +729,7 @@ ConstString Function::GetName() const {
729729
ConstString Function::GetNameNoArguments() const {
730730
return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments);
731731
}
732+
733+
const std::optional<DemangledNameInfo> &Function::GetDemangledInfo() const {
734+
return m_mangled.GetDemangledInfo();
735+
}

lldb/source/Target/Language.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,10 @@ bool Language::IsNilReference(ValueObject &valobj) { return false; }
510510

511511
bool Language::IsUninitializedReference(ValueObject &valobj) { return false; }
512512

513-
bool Language::GetFunctionDisplayName(const SymbolContext *sc,
514-
const ExecutionContext *exe_ctx,
515-
FunctionNameRepresentation representation,
516-
Stream &s) {
513+
bool Language::GetFunctionDisplayName(
514+
const SymbolContext *sc, const ExecutionContext *exe_ctx,
515+
FunctionNameRepresentation representation, Stream &s,
516+
const FormatEntity::Entry::HighlightSettings &) {
517517
return false;
518518
}
519519

0 commit comments

Comments
 (0)