Skip to content

Commit e40c950

Browse files
committed
[lldb][Format] Add new function basename highlight option to FormatEntity
1 parent f487d1f commit e40c950

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
@@ -199,13 +199,41 @@ struct Entry {
199199
return true;
200200
}
201201

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

211239
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"
@@ -531,6 +532,9 @@ class Function : public UserID, public SymbolContextScope {
531532

532533
ConstString GetDisplayName() const;
533534

535+
/// Retrieve \c DemangledNameInfo of the demangled name held by this object.
536+
const std::optional<DemangledNameInfo> &GetDemangledInfo() const;
537+
534538
const Mangled &GetMangled() const { return m_mangled; }
535539

536540
/// 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
@@ -1661,7 +1661,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16611661

16621662
if (language_plugin)
16631663
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1664-
sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1664+
sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss,
1665+
entry.highlight);
16651666

16661667
if (language_plugin_handled) {
16671668
s << ss.GetString();
@@ -1697,7 +1698,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16971698
if (language_plugin)
16981699
language_plugin_handled = language_plugin->GetFunctionDisplayName(
16991700
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1700-
ss);
1701+
ss, entry.highlight);
17011702

17021703
if (language_plugin_handled) {
17031704
s << ss.GetString();
@@ -1731,7 +1732,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
17311732

17321733
if (language_plugin)
17331734
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1734-
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
1735+
sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss,
1736+
entry.highlight);
17351737

17361738
if (language_plugin_handled) {
17371739
s << ss.GetString();
@@ -2074,6 +2076,64 @@ static const Definition *FindEntry(const llvm::StringRef &format_str,
20742076
return parent;
20752077
}
20762078

2079+
/// Parses a single highlighting format specifier.
2080+
///
2081+
/// Example syntax for such specifier:
2082+
/// \code
2083+
/// ${function.name-with-args:%highlight_basename(ansi.fg.green)}
2084+
/// \endcode
2085+
///
2086+
/// In the above snippet, the `function.name-with-args` frame-format
2087+
/// variable will have its basename highlighted in green.
2088+
static llvm::Expected<Entry::HighlightSettings>
2089+
ParseHighlightSettings(const Entry &entry) {
2090+
// FIXME: support other function.name-XXX types as well
2091+
if (entry.type != Entry::Type::FunctionNameWithArgs)
2092+
return llvm::createStringError(
2093+
"The 'highlight_basename' format can only be used on "
2094+
"${function.name-with-args}");
2095+
2096+
llvm::StringRef format = entry.printf_format;
2097+
if (!format.consume_front("highlight_"))
2098+
return llvm::createStringError(
2099+
"Expected 'highlight_' prefix not found in: %s.",
2100+
entry.printf_format.c_str());
2101+
2102+
Entry::HighlightSettings settings;
2103+
if (format.consume_front("basename")) {
2104+
settings.kind = Entry::HighlightSettings::Kind::Basename;
2105+
} else {
2106+
return llvm::createStringError(
2107+
"Unsupported highlight kind detected in: %s. "
2108+
"Currently supported: basename",
2109+
entry.printf_format.c_str());
2110+
}
2111+
2112+
llvm::SmallVector<llvm::StringRef, 1> matches;
2113+
// TODO: support ${ansi.XXX} syntax. ExtractVariableInfo needs
2114+
// to be adjusted to support nested '{}'.
2115+
llvm::Regex color_pattern{R"(^\(([a-z\.]+)\)$)"};
2116+
if (!color_pattern.match(format, &matches))
2117+
return llvm::createStringError(
2118+
"Couldn't find valid color variable in: %s. "
2119+
"Expected format: %%highlight_basename(ansi.some-color)",
2120+
entry.printf_format.c_str());
2121+
2122+
assert(matches.size() == 2);
2123+
2124+
std::string color_format = ("${" + matches[1] + "}").str();
2125+
std::string terminal_code = ansi::FormatAnsiTerminalCodes(color_format);
2126+
if (terminal_code.empty() || terminal_code == color_format)
2127+
return llvm::createStringError("Invalid color variable '%s' found in: %s",
2128+
color_format.c_str(),
2129+
entry.printf_format.c_str());
2130+
2131+
settings.prefix = std::move(terminal_code);
2132+
settings.suffix = ansi::FormatAnsiTerminalCodes("${ansi.normal}");
2133+
2134+
return settings;
2135+
}
2136+
20772137
static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
20782138
uint32_t depth) {
20792139
Status error;
@@ -2229,6 +2289,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
22292289
if (error.Fail())
22302290
return error;
22312291
bool verify_is_thread_id = false;
2292+
bool parse_highlight_settings = false;
22322293
Entry entry;
22332294
if (!variable_format.empty()) {
22342295
entry.printf_format = variable_format.str();
@@ -2294,6 +2355,8 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
22942355
clear_printf = true;
22952356
} else if (entry.printf_format == "tid") {
22962357
verify_is_thread_id = true;
2358+
} else if (entry.printf_format.find("highlight_") == 0) {
2359+
parse_highlight_settings = true;
22972360
} else {
22982361
error = Status::FromErrorStringWithFormat(
22992362
"invalid format: '%s'", entry.printf_format.c_str());
@@ -2335,6 +2398,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
23352398
"the 'tid' format can only be used on "
23362399
"${thread.id} and ${thread.protocol_id}");
23372400
}
2401+
} else if (parse_highlight_settings) {
2402+
auto highlight_or_err = ParseHighlightSettings(entry);
2403+
if (highlight_or_err) {
2404+
entry.highlight = std::move(*highlight_or_err);
2405+
entry.printf_format.clear();
2406+
} else {
2407+
error = Status::FromError(highlight_or_err.takeError());
2408+
}
23382409
}
23392410

23402411
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
@@ -712,3 +712,7 @@ ConstString Function::GetName() const {
712712
ConstString Function::GetNameNoArguments() const {
713713
return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments);
714714
}
715+
716+
const std::optional<DemangledNameInfo> &Function::GetDemangledInfo() const {
717+
return m_mangled.GetDemangledInfo();
718+
}

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)