#include #include #include #include #include #include #include #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(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(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()) { 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()) { 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, 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, 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, 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, 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, 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, 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& 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 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 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 children; std::unordered_set 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 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 children; for (const auto& [scope_id, scope] : all_scopes) { if (scope.parent) { children.emplace(*scope.parent, scope_id); } } std::function 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