787 lines
26 KiB
C++
787 lines
26 KiB
C++
#include <algorithm>
|
|
#include <functional>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
#include "./debug_printer.hpp"
|
|
|
|
namespace
|
|
{
|
|
using namespace lsp::language;
|
|
using namespace lsp::language::symbol;
|
|
|
|
const char* AccessModifierToString(ast::AccessModifier access)
|
|
{
|
|
switch (access)
|
|
{
|
|
case ast::AccessModifier::kPublic:
|
|
return "public";
|
|
case ast::AccessModifier::kProtected:
|
|
return "protected";
|
|
case ast::AccessModifier::kPrivate:
|
|
return "private";
|
|
}
|
|
return "public";
|
|
}
|
|
|
|
const char* VariableScopeToString(VariableScope scope)
|
|
{
|
|
switch (scope)
|
|
{
|
|
case VariableScope::kAutomatic:
|
|
return "auto";
|
|
case VariableScope::kStatic:
|
|
return "static";
|
|
case VariableScope::kGlobal:
|
|
return "global";
|
|
case VariableScope::kParameter:
|
|
return "param";
|
|
case VariableScope::kField:
|
|
return "field";
|
|
}
|
|
return "auto";
|
|
}
|
|
} // namespace
|
|
|
|
namespace lsp::language::symbol::debug
|
|
{
|
|
namespace Color
|
|
{
|
|
constexpr const char* Reset = "\033[0m";
|
|
constexpr const char* Bold = "\033[1m";
|
|
constexpr const char* Dim = "\033[2m";
|
|
constexpr const char* Red = "\033[31m";
|
|
constexpr const char* Green = "\033[32m";
|
|
constexpr const char* Yellow = "\033[33m";
|
|
constexpr const char* Blue = "\033[34m";
|
|
constexpr const char* Magenta = "\033[35m";
|
|
constexpr const char* Cyan = "\033[36m";
|
|
constexpr const char* White = "\033[37m";
|
|
constexpr const char* BrightBlue = "\033[94m";
|
|
constexpr const char* BrightMagenta = "\033[95m";
|
|
constexpr const char* BrightGreen = "\033[92m";
|
|
constexpr const char* Gray = "\033[90m";
|
|
}
|
|
|
|
// ==================== PrintOptions ====================
|
|
|
|
PrintOptions PrintOptions::Default()
|
|
{
|
|
return PrintOptions{};
|
|
}
|
|
|
|
PrintOptions PrintOptions::Compact()
|
|
{
|
|
PrintOptions opts;
|
|
opts.compact_mode = true;
|
|
opts.show_details = false;
|
|
opts.show_children = false;
|
|
opts.indent_size = 1;
|
|
return opts;
|
|
}
|
|
|
|
PrintOptions PrintOptions::Verbose()
|
|
{
|
|
PrintOptions opts;
|
|
opts.show_details = true;
|
|
opts.show_children = true;
|
|
return opts;
|
|
}
|
|
|
|
PrintOptions PrintOptions::NoColor()
|
|
{
|
|
PrintOptions opts;
|
|
opts.use_color = false;
|
|
return opts;
|
|
}
|
|
|
|
// ==================== Statistics ====================
|
|
|
|
void Statistics::Compute(const SymbolTable& table)
|
|
{
|
|
auto all_defs = table.all_definitions();
|
|
total_symbols = all_defs.size();
|
|
|
|
symbol_counts.clear();
|
|
scope_counts.clear();
|
|
|
|
for (const auto& ref : all_defs)
|
|
{
|
|
const auto& sym = ref.get();
|
|
symbol_counts[sym.kind()]++;
|
|
}
|
|
|
|
const auto& scopes = table.scopes().all_scopes();
|
|
total_scopes = scopes.size();
|
|
for (const auto& [_, info] : scopes)
|
|
{
|
|
scope_counts[info.kind]++;
|
|
}
|
|
}
|
|
|
|
void Statistics::Print(std::ostream& os, bool use_color) const
|
|
{
|
|
auto color = [use_color](const char* code) {
|
|
return use_color ? code : "";
|
|
};
|
|
|
|
os << "\n";
|
|
os << color(Color::Bold) << "╔════════════════════════════════════════════════════════════╗\n";
|
|
os << "║ STATISTICS ║\n";
|
|
os << "╚════════════════════════════════════════════════════════════╝"
|
|
<< color(Color::Reset) << "\n\n";
|
|
|
|
os << color(Color::Bold) << "Overview:" << color(Color::Reset) << "\n";
|
|
os << " Total Symbols: " << color(Color::Cyan) << total_symbols << color(Color::Reset) << "\n";
|
|
os << " Total Scopes: " << color(Color::Cyan) << total_scopes << color(Color::Reset) << "\n";
|
|
os << "\n";
|
|
|
|
os << color(Color::Bold) << "Symbol Distribution:" << color(Color::Reset) << "\n";
|
|
|
|
struct KindInfo
|
|
{
|
|
SymbolKind kind;
|
|
const char* name;
|
|
const char* icon;
|
|
};
|
|
|
|
KindInfo kinds[] = {
|
|
{ SymbolKind::Namespace, "Namespace", "🗃️" },
|
|
{ SymbolKind::Class, "Classes", "🏛️" },
|
|
{ SymbolKind::Function, "Functions", "🎄" },
|
|
{ SymbolKind::Method, "Methods", "🪀" },
|
|
{ SymbolKind::Property, "Properties", "📋" },
|
|
{ SymbolKind::Field, "Fields", "📌" },
|
|
{ SymbolKind::Variable, "Variables", "📊" },
|
|
{ SymbolKind::Constant, "Constants", "🔒" }
|
|
};
|
|
|
|
for (const auto& kind_info : kinds)
|
|
{
|
|
auto it = symbol_counts.find(kind_info.kind);
|
|
if (it != symbol_counts.end() && it->second > 0)
|
|
{
|
|
os << " " << kind_info.icon << " "
|
|
<< std::setw(15) << std::left << kind_info.name
|
|
<< ": " << color(Color::BrightBlue) << std::setw(5) << std::right
|
|
<< it->second << color(Color::Reset) << "\n";
|
|
}
|
|
}
|
|
|
|
if (!scope_counts.empty())
|
|
{
|
|
os << "\n"
|
|
<< color(Color::Bold) << "Scope Distribution:" << color(Color::Reset) << "\n";
|
|
for (const auto& [kind, count] : scope_counts)
|
|
{
|
|
os << " " << std::setw(12) << std::left << static_cast<int>(kind)
|
|
<< ": " << color(Color::BrightMagenta) << count << color(Color::Reset) << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================== DebugPrinter ====================
|
|
|
|
DebugPrinter::DebugPrinter(const SymbolTable& table, const PrintOptions& options) : table_(table), options_(options)
|
|
{
|
|
stats_.Compute(table_);
|
|
}
|
|
|
|
void DebugPrinter::PrintSeparator(std::ostream& os, char ch, int width) const
|
|
{
|
|
for (int i = 0; i < width; ++i)
|
|
os << ch;
|
|
os << "\n";
|
|
}
|
|
|
|
std::string DebugPrinter::Color(const char* color_code) const
|
|
{
|
|
return options_.use_color ? color_code : "";
|
|
}
|
|
|
|
std::string DebugPrinter::Bold(const std::string& text) const
|
|
{
|
|
return options_.use_color ? std::string(Color::Bold) + text + Color::Reset : text;
|
|
}
|
|
|
|
std::string DebugPrinter::Dim(const std::string& text) const
|
|
{
|
|
return options_.use_color ? std::string(Color::Dim) + text + Color::Reset : text;
|
|
}
|
|
|
|
std::string DebugPrinter::Indent(int depth) const
|
|
{
|
|
return std::string(static_cast<size_t>(depth * options_.indent_size), ' ');
|
|
}
|
|
|
|
std::string DebugPrinter::FormatLocation(const ast::Location& loc) const
|
|
{
|
|
std::ostringstream oss;
|
|
oss << loc.start_line << ":" << loc.start_column << "-" << loc.end_line << ":" << loc.end_column;
|
|
return oss.str();
|
|
}
|
|
|
|
std::string DebugPrinter::FormatSymbolKind(SymbolKind kind) const
|
|
{
|
|
switch (kind)
|
|
{
|
|
case SymbolKind::Namespace:
|
|
return "Namespace";
|
|
case SymbolKind::Class:
|
|
return "Class";
|
|
case SymbolKind::Method:
|
|
return "Method";
|
|
case SymbolKind::Property:
|
|
return "Property";
|
|
case SymbolKind::Field:
|
|
return "Field";
|
|
case SymbolKind::Function:
|
|
return "Function";
|
|
case SymbolKind::Variable:
|
|
return "Variable";
|
|
case SymbolKind::Constant:
|
|
return "Constant";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
std::string DebugPrinter::FormatScopeKind(ScopeKind kind) const
|
|
{
|
|
switch (kind)
|
|
{
|
|
case ScopeKind::kGlobal:
|
|
return "Global";
|
|
case ScopeKind::kUnit:
|
|
return "Unit";
|
|
case ScopeKind::kClass:
|
|
return "Class";
|
|
case ScopeKind::kFunction:
|
|
return "Function";
|
|
case ScopeKind::kAnonymousFunction:
|
|
return "AnonymousFunction";
|
|
case ScopeKind::kBlock:
|
|
return "Block";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
std::string DebugPrinter::SymbolIcon(SymbolKind kind) const
|
|
{
|
|
switch (kind)
|
|
{
|
|
case SymbolKind::Namespace:
|
|
return "🗃️";
|
|
case SymbolKind::Class:
|
|
return "🏛️";
|
|
case SymbolKind::Method:
|
|
return "🔧"; // Default, will be overridden in GetSymbolIcon
|
|
case SymbolKind::Property:
|
|
return "📋";
|
|
case SymbolKind::Field:
|
|
return "📌";
|
|
case SymbolKind::Function:
|
|
return "🌲"; // Default, will be overridden in GetSymbolIcon
|
|
case SymbolKind::Variable:
|
|
return "📊";
|
|
case SymbolKind::Constant:
|
|
return "🔒";
|
|
default:
|
|
return "❓";
|
|
}
|
|
}
|
|
|
|
std::string DebugPrinter::GetSymbolIcon(const Symbol& symbol) const
|
|
{
|
|
// Check if it's a Method with declaration/implementation distinction
|
|
if (const auto* method = symbol.As<Method>())
|
|
{
|
|
bool has_decl = method->declaration_range.start_line != 0;
|
|
bool has_impl = method->implementation_range.has_value();
|
|
|
|
if (has_decl && has_impl)
|
|
{
|
|
// Has both declaration and implementation
|
|
if (symbol.selection_range().start_line == method->declaration_range.start_line)
|
|
{
|
|
return "🔷"; // Declaration: blue diamond (interface/contract)
|
|
}
|
|
else if (has_impl && symbol.selection_range().start_line == method->implementation_range->start_line)
|
|
{
|
|
return "🔶"; // Implementation: orange diamond (concrete/실행)
|
|
}
|
|
}
|
|
return "🔧"; // Default method icon (wrench/tool)
|
|
}
|
|
|
|
// Check if it's a Function with declaration/implementation distinction
|
|
if (const auto* func = symbol.As<Function>())
|
|
{
|
|
bool has_decl = func->declaration_range.start_line != 0;
|
|
bool has_impl = func->implementation_range.has_value();
|
|
|
|
if (has_decl && has_impl)
|
|
{
|
|
// Has both declaration and implementation
|
|
if (symbol.selection_range().start_line == func->declaration_range.start_line)
|
|
{
|
|
return "🌳"; // Declaration: deciduous tree (interface/skeleton)
|
|
}
|
|
else if (has_impl && symbol.selection_range().start_line == func->implementation_range->start_line)
|
|
{
|
|
return "🎄"; // Implementation: christmas tree (decorated/complete)
|
|
}
|
|
}
|
|
return "🌲"; // Default function icon (evergreen tree)
|
|
}
|
|
|
|
// For other types, use the default icon
|
|
return SymbolIcon(symbol.kind());
|
|
}
|
|
|
|
std::string DebugPrinter::ColorizeSymbolKind(SymbolKind kind) const
|
|
{
|
|
return Color(Color::Blue) + FormatSymbolKind(kind) + Color(Color::Reset);
|
|
}
|
|
|
|
std::string DebugPrinter::ColorizeSymbolName(const std::string& name, SymbolKind /*kind*/) const
|
|
{
|
|
return Color(Color::Yellow) + name + Color(Color::Reset);
|
|
}
|
|
|
|
void DebugPrinter::PrintHeader(const std::string& title, std::ostream& os) const
|
|
{
|
|
os << "\n"
|
|
<< Bold(title) << "\n";
|
|
PrintSeparator(os, '=', 60);
|
|
}
|
|
|
|
void DebugPrinter::PrintSubHeader(const std::string& title, std::ostream& os) const
|
|
{
|
|
os << "\n"
|
|
<< Bold(title) << "\n";
|
|
PrintSeparator(os, '-', 40);
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbolData(const Symbol& symbol, std::ostream& os, int depth)
|
|
{
|
|
std::visit(
|
|
[&](const auto& data) {
|
|
// Tree connector prefix based on depth
|
|
std::string prefix;
|
|
if (depth > 0)
|
|
{
|
|
prefix = Indent(depth - 1) + Color(Color::Gray) + "├─ " + Color(Color::Reset);
|
|
}
|
|
else
|
|
{
|
|
prefix = "";
|
|
}
|
|
|
|
os << prefix << GetSymbolIcon(symbol) << " "
|
|
<< ColorizeSymbolName(symbol.name(), symbol.kind())
|
|
<< " " << Color(Color::Dim) << "(" << FormatSymbolKind(symbol.kind()) << ")" << Color(Color::Reset);
|
|
|
|
if (options_.show_location)
|
|
{
|
|
os << " " << Color(Color::Gray) << "@ " << FormatLocation(symbol.selection_range()) << Color(Color::Reset);
|
|
}
|
|
|
|
// Type-specific details
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Function>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
os << " " << Color(Color::BrightGreen) << "→ " << (data.return_type ? *data.return_type : "void") << Color(Color::Reset);
|
|
if (!data.parameters.empty())
|
|
{
|
|
os << " " << Color(Color::Dim) << "(";
|
|
for (size_t i = 0; i < data.parameters.size(); ++i)
|
|
{
|
|
const auto& p = data.parameters[i];
|
|
os << p.name;
|
|
if (p.type)
|
|
os << ":" << *p.type;
|
|
if (i + 1 < data.parameters.size())
|
|
os << ", ";
|
|
}
|
|
os << ")" << Color(Color::Reset);
|
|
}
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Method>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
os << " " << Color(Color::Cyan) << "[" << AccessModifierToString(data.access) << "]" << Color(Color::Reset);
|
|
|
|
if (data.is_static)
|
|
os << " " << Color(Color::Yellow) << "static" << Color(Color::Reset);
|
|
|
|
if (data.return_type)
|
|
os << " " << Color(Color::BrightGreen) << "→ " << *data.return_type << Color(Color::Reset);
|
|
|
|
if (!data.parameters.empty())
|
|
{
|
|
os << " " << Color(Color::Dim) << "(";
|
|
for (size_t i = 0; i < data.parameters.size(); ++i)
|
|
{
|
|
const auto& p = data.parameters[i];
|
|
os << p.name;
|
|
if (p.type)
|
|
os << ":" << *p.type;
|
|
if (i + 1 < data.parameters.size())
|
|
os << ", ";
|
|
}
|
|
os << ")" << Color(Color::Reset);
|
|
}
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Property>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
os << " " << Color(Color::Cyan) << "[" << AccessModifierToString(data.access) << "]" << Color(Color::Reset);
|
|
if (data.type)
|
|
os << " " << Color(Color::BrightGreen) << ":" << *data.type << Color(Color::Reset);
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Field>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
os << " " << Color(Color::Cyan) << "[" << AccessModifierToString(data.access) << "]" << Color(Color::Reset);
|
|
if (data.type)
|
|
os << " " << Color(Color::BrightGreen) << ":" << *data.type << Color(Color::Reset);
|
|
if (data.is_static)
|
|
os << " " << Color(Color::Yellow) << "static" << Color(Color::Reset);
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Variable>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
os << " " << Color(Color::Dim) << "(" << VariableScopeToString(data.storage) << ")" << Color(Color::Reset);
|
|
if (data.type)
|
|
os << " " << Color(Color::BrightGreen) << ":" << *data.type << Color(Color::Reset);
|
|
if (data.has_initializer)
|
|
os << " " << Color(Color::Gray) << "= ..." << Color(Color::Reset);
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<std::decay_t<decltype(data)>, Constant>)
|
|
{
|
|
if (options_.show_details)
|
|
{
|
|
if (data.type)
|
|
os << " " << Color(Color::BrightGreen) << ":" << *data.type << Color(Color::Reset);
|
|
os << " " << Color(Color::Gray) << "= " << data.value << Color(Color::Reset);
|
|
}
|
|
}
|
|
|
|
os << "\n";
|
|
},
|
|
symbol.data());
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbol(SymbolId id, std::ostream& os, int depth)
|
|
{
|
|
const auto* sym = table_.definition(id);
|
|
if (!sym)
|
|
{
|
|
os << Indent(depth) << "❓ Unknown symbol: " << id << "\n";
|
|
return;
|
|
}
|
|
|
|
PrintSymbolData(*sym, os, depth);
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbolWithChildren(SymbolId id, const std::unordered_multimap<SymbolId, SymbolId>& children, std::ostream& os, int depth, int current_depth)
|
|
{
|
|
if (options_.max_depth >= 0 && current_depth > options_.max_depth)
|
|
return;
|
|
|
|
PrintSymbol(id, os, depth);
|
|
|
|
if (!options_.show_children)
|
|
return;
|
|
|
|
auto range = children.equal_range(id);
|
|
std::vector<SymbolId> child_list;
|
|
for (auto it = range.first; it != range.second; ++it)
|
|
{
|
|
child_list.push_back(it->second);
|
|
}
|
|
|
|
// Sort children by location for consistent output
|
|
std::sort(child_list.begin(), child_list.end(), [this](SymbolId a, SymbolId b) {
|
|
const auto* sym_a = table_.definition(a);
|
|
const auto* sym_b = table_.definition(b);
|
|
if (!sym_a || !sym_b)
|
|
return a < b;
|
|
|
|
auto loc_a = sym_a->selection_range();
|
|
auto loc_b = sym_b->selection_range();
|
|
|
|
if (loc_a.start_line != loc_b.start_line)
|
|
return loc_a.start_line < loc_b.start_line;
|
|
return loc_a.start_column < loc_b.start_column;
|
|
});
|
|
|
|
for (auto child_id : child_list)
|
|
{
|
|
PrintSymbolWithChildren(child_id, children, os, depth + 1, current_depth + 1);
|
|
}
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbolTree(SymbolId id, std::ostream& os, int depth)
|
|
{
|
|
std::unordered_multimap<SymbolId, SymbolId> children;
|
|
for (const auto& [scope_id, scope] : table_.scopes().all_scopes())
|
|
{
|
|
if (scope.owner)
|
|
{
|
|
for (const auto& [_, sym_id] : scope.symbols)
|
|
{
|
|
children.emplace(*scope.owner, sym_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
PrintSymbolWithChildren(id, children, os, depth, 0);
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbolList(std::ostream& os)
|
|
{
|
|
PrintHeader("Symbols", os);
|
|
|
|
auto all_defs = table_.all_definitions();
|
|
|
|
// Group symbols by their parent (class/namespace)
|
|
std::unordered_multimap<SymbolId, SymbolId> children;
|
|
std::unordered_set<SymbolId> has_parent;
|
|
|
|
for (const auto& [scope_id, scope] : table_.scopes().all_scopes())
|
|
{
|
|
if (scope.owner)
|
|
{
|
|
for (const auto& [_, sym_id] : scope.symbols)
|
|
{
|
|
children.emplace(*scope.owner, sym_id);
|
|
has_parent.insert(sym_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find top-level symbols (no parent)
|
|
std::vector<const Symbol*> top_level;
|
|
for (const auto& ref : all_defs)
|
|
{
|
|
const auto& sym = ref.get();
|
|
if (has_parent.find(sym.id()) == has_parent.end())
|
|
{
|
|
top_level.push_back(&sym);
|
|
}
|
|
}
|
|
|
|
// Sort top-level symbols by location
|
|
std::sort(top_level.begin(), top_level.end(), [](const Symbol* a, const Symbol* b) {
|
|
auto loc_a = a->selection_range();
|
|
auto loc_b = b->selection_range();
|
|
if (loc_a.start_line != loc_b.start_line)
|
|
return loc_a.start_line < loc_b.start_line;
|
|
return loc_a.start_column < loc_b.start_column;
|
|
});
|
|
|
|
// Print each top-level symbol with its children
|
|
for (const auto* sym : top_level)
|
|
{
|
|
PrintSymbolWithChildren(sym->id(), children, os, 0, 0);
|
|
}
|
|
}
|
|
|
|
void DebugPrinter::PrintSymbolsByKind(SymbolKind kind, std::ostream& os)
|
|
{
|
|
PrintSubHeader("Symbols of kind: " + FormatSymbolKind(kind), os);
|
|
auto all_defs = table_.all_definitions();
|
|
for (const auto& ref : all_defs)
|
|
{
|
|
const auto& sym = ref.get();
|
|
if (sym.kind() == kind)
|
|
{
|
|
PrintSymbolData(sym, os, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugPrinter::PrintScope(ScopeId id, std::ostream& os, int depth)
|
|
{
|
|
const auto* scope = table_.scopes().scope(id);
|
|
if (!scope)
|
|
{
|
|
os << Indent(depth) << "❓ Unknown scope: " << id << "\n";
|
|
return;
|
|
}
|
|
|
|
os << Indent(depth) << "📁 " << FormatScopeKind(scope->kind)
|
|
<< " (id=" << id << ")";
|
|
if (scope->owner)
|
|
{
|
|
os << " owner=" << *scope->owner;
|
|
}
|
|
os << "\n";
|
|
|
|
for (const auto& [name, sym_id] : scope->symbols)
|
|
{
|
|
os << Indent(depth + 1) << "• " << name << " (id=" << sym_id << ")\n";
|
|
}
|
|
}
|
|
|
|
void DebugPrinter::PrintScopeTree(ScopeId id, std::ostream& os, int depth)
|
|
{
|
|
const auto& all_scopes = table_.scopes().all_scopes();
|
|
std::unordered_multimap<ScopeId, ScopeId> children;
|
|
for (const auto& [scope_id, scope] : all_scopes)
|
|
{
|
|
if (scope.parent)
|
|
{
|
|
children.emplace(*scope.parent, scope_id);
|
|
}
|
|
}
|
|
|
|
std::function<void(ScopeId, int)> dfs = [&](ScopeId sid, int d) {
|
|
PrintScope(sid, os, d);
|
|
auto range = children.equal_range(sid);
|
|
for (auto it = range.first; it != range.second; ++it)
|
|
{
|
|
dfs(it->second, d + 1);
|
|
}
|
|
};
|
|
|
|
dfs(id, depth);
|
|
}
|
|
|
|
void DebugPrinter::PrintScopeHierarchy(std::ostream& os)
|
|
{
|
|
PrintHeader("Scopes", os);
|
|
const auto& scopes = table_.scopes().all_scopes();
|
|
if (scopes.empty())
|
|
{
|
|
os << "No scopes available\n";
|
|
return;
|
|
}
|
|
|
|
auto global = table_.scopes().global_scope();
|
|
if (global == kInvalidScopeId && !scopes.empty())
|
|
{
|
|
global = scopes.begin()->first;
|
|
}
|
|
PrintScopeTree(global, os, 0);
|
|
}
|
|
|
|
void DebugPrinter::FindAndPrint(const std::string& name, std::ostream& os)
|
|
{
|
|
PrintHeader("Search: " + name, os);
|
|
auto matches = table_.FindSymbolsByName(name);
|
|
if (matches.empty())
|
|
{
|
|
os << "No symbols found matching \"" << name << "\"\n";
|
|
return;
|
|
}
|
|
|
|
for (auto id : matches)
|
|
{
|
|
PrintSymbol(id, os);
|
|
}
|
|
}
|
|
|
|
void DebugPrinter::FindAtLocation(const ast::Location& loc, std::ostream& os)
|
|
{
|
|
auto id = table_.FindSymbolAt(loc);
|
|
if (!id)
|
|
{
|
|
os << "No symbol found at location " << FormatLocation(loc) << "\n";
|
|
return;
|
|
}
|
|
PrintSymbol(*id, os);
|
|
}
|
|
|
|
void DebugPrinter::PrintOverview(std::ostream& os)
|
|
{
|
|
PrintHeader("Overview", os);
|
|
os << "Symbols: " << stats_.total_symbols << "\n";
|
|
os << "Scopes: " << stats_.total_scopes << "\n";
|
|
}
|
|
|
|
void DebugPrinter::PrintStatistics(std::ostream& os)
|
|
{
|
|
stats_.Print(os, options_.use_color);
|
|
}
|
|
|
|
void DebugPrinter::PrintAll(std::ostream& os)
|
|
{
|
|
PrintOverview(os);
|
|
PrintSymbolList(os);
|
|
PrintScopeHierarchy(os);
|
|
PrintStatistics(os);
|
|
}
|
|
|
|
// ==================== Free functions ====================
|
|
|
|
void Print(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
printer.PrintAll(os);
|
|
}
|
|
|
|
void PrintOverview(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
printer.PrintOverview(os);
|
|
}
|
|
|
|
void PrintStats(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
printer.PrintStatistics(os);
|
|
}
|
|
|
|
void PrintSymbolTree(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
auto defs = table.all_definitions();
|
|
for (const auto& ref : defs)
|
|
{
|
|
printer.PrintSymbolTree(ref.get().id(), os);
|
|
}
|
|
}
|
|
|
|
void PrintScopeTree(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
printer.PrintScopeHierarchy(os);
|
|
}
|
|
|
|
void Find(const SymbolTable& table, const std::string& name, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Default());
|
|
printer.FindAndPrint(name, os);
|
|
}
|
|
|
|
void PrintCompact(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Compact());
|
|
printer.PrintOverview(os);
|
|
printer.PrintSymbolList(os);
|
|
}
|
|
|
|
void PrintVerbose(const SymbolTable& table, std::ostream& os)
|
|
{
|
|
DebugPrinter printer(table, PrintOptions::Verbose());
|
|
printer.PrintAll(os);
|
|
}
|
|
|
|
} // namespace lsp::language::symbol::debug
|